多线程中的wait和notify

news2025/1/10 21:27:42

1、wait和notify

由于线程之间是抢占式执行的,所以线程之间的执行先后顺序难以预知。但实际上是希望合理的协调多个线程之间的执行先后顺序。

 完成这个协调工作,主要涉及到三个方法

*wait()/wait(long timeout);让当前线程进入等待状态。

*notify()/notifyAll();唤醒在当前对象上等待的线程。

注意:wait,notify,notifyAll都是Object的方法,而之前提到的join()(等待当前线程运行结束)方法是Thread类中的方法,

2、wait方法

wait做的事情:

*使当前执行的代码的线程进行等待。(把线程放到等待队列中) 

*释放当前锁

*满足一定条件就会被唤醒,重新尝试获取这个锁。

wait要搭配synchronized一起使用,脱离synchronized使用wait就会抛出异常。

wait结束等待的条件:

*其他线程调用该对象的notify()方法

*wait等待时间超时(wait提供一个带有timeout参数的版本,来指定等待时间)

*其他线程调用该等待线程的interrupted方法,导致wait抛出InterrputedException异常。

3、 notify方法

notify是唤醒等待线程的方法

*方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的其他线程,对其发出通知notify(),并使它们重新获取该对象的对象锁。

*如果有多个线程等待,则有线程调度器随机挑选一个状态为wait的线程,(并没有“先来后到的原则”)。

*在notify()方法后,当前线程不会马上释放该对象锁,要等待到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁。

4、代码示例:

/**
 * wait()和notify()方法
 */
public class Exe_02 {
    public static void main(String[] args) {
        //定义一个锁对象
        Object locker=new Object();
        //创建调用wait()方法的线程
        Thread t1=new Thread(() ->{
            while(true){
                System.out.println("wait之前");
                try {
                    synchronized (locker) {
                        locker.wait();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("wait之后");
                System.out.println("==============");
            }
        },"t1");
        //创建调用notify方法线程
        Thread t2=new Thread(() ->{
            while(true){
                System.out.println("notify之前");
                //加入锁
                synchronized (locker) {
                    locker.notify();
                }
                System.out.println("notify之后");
                //休眠一会
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"t2");
        //启动线程
        t1.start();
        t2.start();
    }
}

 执行逻辑分析:

 注意:

1、当一个线程调用了wait()之后,就释放掉当前持有的锁,等待被其他线程唤醒。

2、当另一个线程调用了notify()之后, 之前调用wait()的线程被唤醒后,需要重新竞争锁资源然后再从之前wait的位置向下执行自己的逻辑。

4.1、notify()和notifyAll()

notify方法只是随机唤醒某一个线程,这个线程来获取锁;

notifyAll可以一次性唤醒所有等待的线程,这些线程全部去竞争锁,谁先拿到谁先处理。 

 5、观察多线程环境下锁状态

代码示例:

import org.openjdk.jol.info.ClassLayout;

public class Exe_03 {
    //定义一些变量
    private int count;
    private long count1=200;
    private String hello="";
    //定义一个对象变量
    private textExe_03 text03=new textExe_03();

    public static void main(String[] args) throws InterruptedException {
        //创建一个对象的实例
        Object obj=new Object();
        //打印实例布局
        System.out.println("=====任意object对象布局,起初无锁状态");
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());
        System.out.println("延迟三秒开启偏向锁");
        //延迟3秒开启偏向锁
        Thread.sleep(3000);
        //创建本类的实例
        Exe_03 exe_03=new Exe_03();
        //打印实例布局,查看锁状态
        System.out.println("=====打印实例布局,查看锁状态");
        System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
        //调用hashCode后,保存hashCode的值
        exe_03.hashCode();
        //观察现象
        System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
        System.out.println("==============================");
        System.out.println("synchronized加锁");
        //加锁后观察锁信息
        synchronized(exe_03){
            System.out.println("第一层synchronized加锁后");
            System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
            //锁重入,观察锁信息
            synchronized(exe_03){
                System.out.println("第二层synchronized加锁后");
                System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
            }
            //释放里层的锁
            System.out.println("释放内层锁后");
            System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
        }
        //释放所有锁之后
        System.out.println("=========释放所有锁========");
        System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
        System.out.println("==============================");
        //强制执行垃圾回收
        System.gc();
        //观察GC计数
        System.out.println("+++++++调用GC后查看age的值");
        System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
        //打印类布局,调用不同的方法查看
        System.out.println("+++++查看类布局");
        System.out.println(ClassLayout.parseClass(Exe_03.class).toPrintable());
        //打印类对象布局
        System.out.println("+++++查看类对象布局");
        System.out.println(ClassLayout.parseInstance(Exe_03.class).toPrintable());
        synchronized (Exe_03.class){
            //加锁后的类对象
            System.out.println("+++++对类对象加锁后,不同的对象获取锁,观察锁升级为thin lock");
            System.out.println(ClassLayout.parseInstance(Exe_03.class).toPrintable());
        }
        //释放锁之后的类对象
        System.out.println("+++++释放锁后");
        System.out.println(ClassLayout.parseInstance(Exe_03.class).toPrintable());
        System.out.println("+++++多个锁线程参与锁竞争,观察锁状态+++++");
        Thread t1=new Thread(() ->{
            synchronized(exe_03){
                System.out.println("++++++在线程A中参与锁竞争,观察锁状态++++++");
                System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
            }
        });
        t1.start();
        Thread t2=new Thread(() ->{
            synchronized(exe_03){
                System.out.println("++++++在线程B中参与锁竞争,观察锁状态++++++");
                System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
            }
        });
        t2.start();
        Thread t3=new Thread(() ->{
            synchronized(exe_03){
                System.out.println("++++++在线程C中参与锁竞争,观察锁状态++++++");
                System.out.println(ClassLayout.parseInstance(exe_03).toPrintable());
            }
        });
        t3.start();
    }
}
class textExe_03{

}

 查看运行结果:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/680334.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【每日算法 数据结构(C++)】—— 02 | 数组的并交集(解题思路、流程图、代码片段)

文章目录 01 | 👑 题目描述02 | 🔋 解题思路交集并集 03 | 🧢 代码片段交集并集 When you feel like giving up, remember why you started. 当你想放弃时,请记住为什么你开始 01 | 👑 题目描述 给你两个数组&#xff…

学习机组过程中的疑难问题与解决 -----(1)

本文章是在学习计算机组成原理过程中个人感觉需要理解与记忆的问题,还有一些在学习过程中自己产生的疑问以及解答,本文章可能排版不良,精力有限,还请见谅 第一章: (1)MAR的位数对应着存储单元的个数&#…

前端三剑客简介

文章目录 css的导入方式CSS选择器 JavascriptJavascript的引入方式输出语句变量数据类型javascript对象String对象 BOM对象DOM对象 W3C标准:网页主要由三部分组成 结构:html表现:css,层叠样式表行为:JavaScript css的…

VulnHub项目:Nagini

1、靶机地址: HarryPotter: Nagini ~ VulnHub 本篇为哈利波特死亡圣器系列的第二部,该靶机中存在3个魂器,依次找到它们,摧毁它们! 2、渗透过程 首先收集靶机ip和确定攻击机ip,同时探测靶机开放端口 存在…

一个简单案例理解为什么在多线程的应用中要使用互斥锁

需求:使用10个线程,同时对一个值count进行加一操作,每个线程对count加100000次,最终使得count1000000 第一版代码:不加锁 ​​​lock.c #include<stdio.h> #include<pthread.h>#define THREAD_COUNT 10void *thread_callback(void *arg){int *pcount(int*)arg;in…

Unity例子——第一人称视角的角色控制器

本文是为了前文Unity四元组的举例示范&#xff0c;为了让读者更好地理解。 效果是实现一个可以由鼠标进行方向操作&#xff0c;键盘进行移动操作的任务。 此为效果视频&#xff1a; 1687597097844 下面进行教学&#xff1a; 一、搭建简单场景 新建一个场景&#xff0c;放置一…

SonarQube(sonar-scanner)+GitLab(gitlab-runner)实现提交代码自动扫描项目代码

安装gitlab-runner 插件挂载目录 mkdir -p /data/gitlab-runner/configdocker run -d --name gitlab-runner \ -v /data/gitlab-runner/config:/etc/gitlab-runner \ -v /var/run/docker.sock:/var/run/docker.sock \ --restart always \ --privilegedtrue \ gitlab/gitlab-r…

【数据库必备知识】索引和事务

数据库系列文章 1. 零基础带你快速上手SQL语言2. 玩转表及其数据3. 上手表设计 4. 索引和事务 目录 &#x1f4d6;前言 1. 索引 1.1 索引的概念 1.2 索引的作用 1.3 索引的使用场景 1.4 索引的使用 1.5 索引背后的数据结构(B树) 2. 事务 2.1 事务的概念 2.2 数据库使…

Centos7安装Python3.10

Centos7用yum安装的Python3版本比较旧&#xff0c;想要安装最新版本的Python3需要自己动手编译安装。下面就来讲讲安装步骤&#xff0c;主要分为这么几个步骤&#xff0c;依赖→下载→编译→配置。另外所有操作都是在root用户下进行。 依赖 编译Python源码需要依赖许多库&…

spring.cache 随笔0 集成设计

0. 最近感觉 “困意驱动睡眠” 也有他的意义 spring cache学习&#xff08;一&#xff09;&#xff1a;spring cache注解简单了解 Java Caching JSR107介绍 同样&#xff0c;本章也会简单的集成redisson作为缓存服务 1. 从我们自己写的javaConfig开始吧 Configuration // 这…

Java——文件操作

文件操作 1、File类概述2、File类的常用APIFile类的判断文件类型、获取文件信息功能创建文件、删除文件功能遍历文件夹文件搜索 3、字符集常见字符集字符集的编码、解码操作 4、IO流分类5、字节流的使用文件字节输入流文件字节输出流文件拷贝 6、字符类的使用文件字符输入流文件…

“前端已死”

一、一些迹象 逛社区&#xff0c;偶然看到了这张图片&#xff1a; 嗯……我眉头一皱&#xff0c;久久不语&#xff0c;心想&#xff0c;有这么夸张吗&#xff0c;假的吧&#xff1f; 突然想到&#xff0c;最近我在社区发了个前端招聘的信息&#xff0c;结果简历漫天纷飞&…

【C++】auto_ptr为何被唾弃?以及其他智能指针的学习

搭配异常可以让异常的代码更简洁 文章目录 智能指针 内存泄漏的危害 1.auto_ptr(非常不建议使用) 2.unique_ptr 3.shared_ptr 4.weak_ptr总结 智能指针 C中为什么会需要智能指针呢&#xff1f;下面我们看一下样例&#xff1a; int div() {int a, b;cin >&g…

图表制作办公首选--实用图表工具Echars

实用图表工具Echars 前言 由于工作的需要&#xff0c;在写材料的时候需要使用到柱状图、饼状图、折线图等等展示数据&#xff0c;可以使用PPT等办公软件构建出图表&#xff0c;在这里可以使用更加方便、更加美观的工具Echars。 Echars图表使用 Echars官网&#xff1a;Ecahr…

二叉平衡树之红黑树

目录 1.概念 2.性质 3.节点的定义 4.插入 1.按照二叉搜索树规则插入结点 2.调整颜色 1.uncle存在且为红色 2.uncle不存在或者为黑 cur为 3.根节点改为黑色 5.验证 6.比较 7.应用 1.概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存…

【计算机网络】计算机网络期末自测题(一)

目录 一、 填空题&#xff1a;(20 分&#xff0c;每空 1 分) 二、 选择题(20 分&#xff0c;每小题 1 分) 三、不定项选择题 (10 分&#xff0c;每小题 1 分) 四、名词解释 (15 分&#xff0c;每小题 3 分) 五、简答题 (25 分) 得分 一、 填空题&#xff1a;(20 分&#xff…

【C++】STL——string类详解

&#x1f680; 作者简介&#xff1a;一名在后端领域学习&#xff0c;并渴望能够学有所成的追梦人。 &#x1f681; 个人主页&#xff1a;不 良 &#x1f525; 系列专栏&#xff1a;&#x1f6f8;C &#x1f6f9;Linux &#x1f4d5; 学习格言&#xff1a;博观而约取&#xff0…

混合策略改进的哈里斯鹰优化算法-附代码

混合策略改进的哈里斯鹰优化算法 文章目录 混合策略改进的哈里斯鹰优化算法1.哈里斯鹰优化算法2.改进哈里斯鹰优化算法2.1 初始化种群的改进2.1.1 初始种群多样化2.1.2 初始种群精英化 2.2 逃逸能量递减机制的改进2.4 拉普拉斯交叉算子策略 3.实验结果4.参考文献5.Matlab代码6.…

6.17 、Java初级:锁

1 同步锁 1.1 前言 经过前面多线程编程的学习,我们遇到了线程安全的相关问题,比如多线程售票情景下的超卖/重卖现象. 上节笔记点这里-进程与线程笔记 我们如何判断程序有没有可能出现线程安全问题,主要有以下三个条件: 在多线程程序中 有共享数据 多条语句操作共享数据 多…

移动web-渐变

渐变 使用场景&#xff1a;使用background-image属性实现渐变背景效果 代码&#xff1a;background-image: linear-gradient(参数1,参数2,参数3...); (默认的方位从上到下) 参数1 方位名词: to right, to left 角度deg: 直接写度数 参数2 颜色1 参数3 颜色2... 注意&#xff…