JAVA线程 -- wait notify notifyAll

2023-11-17

在通常的代码中实现线程互斥用的较多的是synchronized()。
synchronized(this)与synchronized(static Object)的区别,synchronized就是针对内存区块申请内存锁,this关键字代表类的一个对象,所以其内存锁是针对相同对象的互斥操作,而static成员属于类专有,其内存空间为该类所有成员共有,这就导致synchronized()对static成员加锁,相当于对类加锁,也就是在该类的所有成员间实现互斥,在同一时间只有一个线程可访问该类的实例。
wait方法
    调用线程的sleep,yield方法时,线程并不会让出对象锁,wait却不同。
    wait函数必须在同步代码块中调用(也就是当前线程必须持有对象的锁),他的功能是这样的:
        我累了,休息一会儿,对象的锁你们拿去用吧,CPU也给你们。
    调用了wait函数的线程会一直等待,直到有其他线程调用了同一个对象的notify或者notifyAll方法才能被唤醒,需要注意的是:被唤醒并不代表立即获得对象的锁。也就是说,一个线程调用了对象的wait方法后,他需要等待两件事情的发生:
    1. 有其他线程调用同一个对象的notify或者notifyAll方法(调用notify/notifyAll方法之前)
    2. 被唤醒之后重新获得对象的锁(调用notify/notifyAll方法之后)才能继续往下执行后续动作。
    如果一个线程调用了某个对象的wait方法,但是后续并没有其他线程调用该对象的notify或者notifyAll方法,则该线程将会永远等下去…
notify和notifyAll方法
    notify/notifyAll方法也必须在同步代码块中调用(也就是调用线程必须持有对象的锁),他们的功能是这样的:
    女士们,先生们请注意,锁的对象我即将用完,请大家醒醒,准备一下,马上你们就能使用锁了。
    不同的是,notify方法只会唤醒一个正在等待的线程(至于唤醒谁,不确定!),而notifyAll方法会唤醒所有正在等待的线程。还有一点需要特别强调:调用notify和notifyAll方法后,当前线程并不会立即放弃锁的持有权,而必须要等待当前同步代码块执行完才会让出锁。
    如果一个对象之前没有调用wait方法,那么调用notify方法是没有任何影响的。
下面来看看实例:

<span style="font-size:14px;">package thread;

import java.util.concurrent.TimeUnit;

public class Wait_Notify {
	
	public static Object thread = new Object(); 
	static class Thread1 implements Runnable  
    {  
        @Override  
        public void run()  
        {  
            synchronized(thread)  
            {  
                System.out.println("start "+Thread.currentThread().getName()+" is running.");  
                try  
                {  
                	thread.wait();  
                }  
                catch (InterruptedException e)  
                {  
                    e.printStackTrace();  
                }  
                System.out.println("end "+Thread.currentThread().getName()+" get the lock.");  
            }  
        }  
    }  
  
    static class Thread2 implements Runnable  
    {  
        @Override  
        public void run()  
        {  
            synchronized(thread)  
            {  
                System.out.println("start "+Thread.currentThread().getName()+" is running.");  
                thread.notify();  
                System.out.println(Thread.currentThread().getName()+" invoke notify.");  
                System.out.println(Thread.currentThread().getName()+" release the lock.");  
            }  
           
        }  
    }  
  
    public static void main(String[] args) throws InterruptedException  
    {  
        Thread thread1 = new Thread(new Thread1());  
        Thread thread2 = new Thread(new Thread2());  
        thread1.start();  
        TimeUnit.SECONDS.sleep(1);  
        thread2.start();  
    }  

}
</span>

多次运行结果:

<span style="font-size:14px;">Thread-0 is running.  
Thread-1 is running.  
Thread-1 invoke notify()  
Thread-1 release the lock.  
Thread-0 get the lock.  </span>

    可以看到当Thread-0调用了wait方法后就释放了object锁,Thread-1获取锁之后调用notify的释放锁,但是这个时候Thread没有立刻获取object锁,而是等到了Thread-1的同步块退出之后才获取了object的锁。
    如果将class Thread2中的 System.out.println(Thread.currentThread().getName()+" release the lock.");这句放在synchronized(object)的外面,有可能出现如下的运行结果:
<span style="font-size:14px;">start Thread-0 is running.
start Thread-1 is running.
Thread-1 invoke notify.
Thread-1 release the lock.
end Thread-0 get the lock.</span>

   这是因为,当Thread-1释放了object锁并且退出了同步块之后Thread-0立刻获取了锁,而这时候两个线程的打印语句的顺序就随机了。


总结:

在Java中,可以通过配合调用Object对象的wait方法和notify方法或notifyAll方法来实现线程间的协作通信。在线程中调用wait方法,将阻塞等待其他线程的通知(其他线程调用notify方法或notifyAll方法),在线程中调用notify方法或notifyAll方法,将通知其他线程从wait方法处返回。 


Object.wait()与Object.notify()必须要与同步块或同步方法(synchronized块或者synchronized方法)一起使用,也就是wait与notify是针对已经获取了Object锁进行操作。

从语法角度来说就是Object.wait(),Object.notify必须在同步块或同步方法内。

从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。相应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的同步块或同步方法中执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。

这样就提供了在线程间同步、唤醒的操作。Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JAVA线程 -- wait notify notifyAll 的相关文章

  • Linux 系统编程之select

    Linux 系统编程之select select 允许单个程序监听多个文件描述符 直到一个或者多个文件描述符准备就绪不阻塞系统 常常用于解决阻塞型的程序 相关代码 According to POSIX 1 2001 include
  • Linux之文件上传和下载(两种方式)

    前言 由于工作需要 整理了一下Linux与Windows之间文件的上传和下载方式 一 rz上传 sz下载文件 rz是将Windows系统上的文件上传到Linux下 sz是将Linux系统下的文件上传到Windows下 1 rz案例 输入rz
  • 【STM32】NRF24L01模块的收发调试

    NRF24L01 发送端 c文件 发送端 h文件 接收端 c文件 接收端 h文件 接收端main函数 总结 这里我是用了两块板子来做通信实验 这里我就直接贴发送端和接收端的 c h文件 一个是用标准库写的一个是hal库写的 只是两块板子引脚
  • python 生成随机字符串(数字+字母+特殊字符)

    方法一 usr bin env python coding utf 8 import random import string 第一种方法 seed 1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHI
  • 【数据可视化】基于Streamlit制作的时间序列数据分析APP(上手简单,附可运行源码)

    基于Streamlit制作的时间序列数据分析APP 1 为什么要使用Streamlit 官网介绍链接 在数据科学领域 一方面 当我们在学习或者实践机器学习算法的时候 需要进行一些参数调整 另一方面 我们也希望将最后的成果通过一种友好的可视化
  • Python入门:什么是爬虫,怎么玩爬虫?

    python爬虫入门 什么是爬虫 怎么玩爬虫 看到这两只爬虫没有 两只爬虫 两只爬虫 跑得快 跑得快 一只没有 不好意思 跑题了 别误会 今天不是要教你怎么玩上面这两只沙雕玩意 今天 我们正式从0到1 轻松学会 python 爬虫 接下来
  • STC单片机代码烧录

    说明 两种烧录方式 一 ISP烧录 原料 usb装ttl 单片机 开发板 stc isp 15xx v6 85H exe 群文件有 1 接线vcc vcc gnd gnd RX TX TX RX 2 安装CH340驱动 群文件有 3 将TT
  • 给电脑重装系统后修改远程桌面端口的方法

    怎么修改远程端口 我们都知道系统默认的远程桌面端口是3389 可提供 远程桌面 等连接工具来连接到远程的服务器上面 但是近期有部分Win11用户想要修改这一端口 那么应该如何操作呢 还不清楚应该如何操作的朋友们 可以试试用下面的方法进行操作
  • k8s-3:集群的三种网络

    k8s集群一共有三种网络模型 Node IP Node节点的IP地址 即物理网卡的IP地址 Pod IP Pod的IP地址 即docker容器的IP地址 此为虚拟IP地址 Cluster IP Service的IP地址 此为虚拟IP地址 N
  • 内网搭建yum源

    一 安装httpd服务 yum install y httpd systemctl start httpd systemctl status httpd 二 挂载centos7的ISO文件 2 1 提前准备好ISO文件 下载链接 https
  • chatgpt使用技巧,提示词技巧,效率提问,如何更好的使用chatgpt

    说明 什么是提示词 提示词有什么作用 提示词是我们向语言模型提出问题时使用的基本词语 使用提示词可以让模型更好地理解我们的需求 得出更准确 有用的回答 与直接向模型提问相比 使用提示词需要明确我们的要求 并提供一些背景资料和实例等详细信息
  • NLP实战:基于Pytorch的文本分类入门实战

    目录 一 前期准备 1 环境准备 2 加载数据 二 代码实战 1 构建词典 2 生成数据批次和迭代器 3 定义模型 4 定义实例 5 定义训练函数与评估函数 6 拆分数据集并运行模型 三 使用测试数据集评估模型 四 总结 本文为 365天深
  • 解决MarkDownPad表格显示的问题,以及代码结构显示的问题

    中文版 工具 选项 Markdown Markdown处理器 改为 Markdown 扩展 即可 选择GitHub Flavored Markdown Offling 英文版 Tools Options MarkDown Markdown
  • linux动态链接库的创建

    如下实际操作形式
  • PuTTY和WinSCP

    PuTTY和WinSCP是管理OpenWRT固件的路由器必备工具 WinSCP的优势在于支持多语言 可在图形化界面下管理Linux系统里的文件和存储多个会话设置以方便多设备多用户登录 PuTTY的优势在 于提供与Linux终端一样的命令行控
  • 互联网产品经理面试二三事

    http www masterchat cn article article 250 1 html 摘要 面试是一件辛苦的工作 相信被面试也是一件不容易的事 找到一个适合企业的人才 并不一定是最优秀的 很不容易 尤其在互联网领域 优秀的人才
  • css设置文字垂直居中

    vertical align top 不断调整该值 就可以调整上下位置哦 如 上 vertical align top 中 vertical align middle 下 vertical align bottom 特殊 vertical
  • 数据预处理--缺失值和异常值的处理

    处理缺失数据的方法 1 用平均值 中值 分位数 众数 随机值等替代 如果预计该变量对于学习模型效果影响不大 可以对unknown值赋众数 这里认为变量都对学习模型有较大影响 效果一般 因为等于人为增加了噪声 不建议采取此法 数值型的话 均值
  • 正则表达式生成器

    正则表达式生成器 智能提示和表达式含义解析 高亮显示匹配项内容 高端智能正则表达式生成工具 支持生成语言 Javascript PHP Go rb python Java objc C 等 下载地址 https download csdn

随机推荐

  • 剑指 Offer :001整数除法

    题目描述 给定两个整数 a 和 b 求它们的除法的商 a b 要求不得使用乘号 除号 以及求余符号 注意 整数除法的结果应当截去 truncate 其小数部分 例如 truncate 8 345 8 以及 truncate 2 7335 2
  • 线性回归案例分析-预测PM2.5

    作业说明 给定训练集train csv 要求根据前9个小时的空气监测情况预测第10个小时的PM2 5含量 训练集介绍 1 CSV文件 包含台湾丰原地区240天的气象观测资料 取每个月前20天的数据做训练集 12月X20天 240天 每月后1
  • 特征工程:特征构造以及时间序列特征构造

    数据和特征决定了机器学习的上限 而模型和算法只是逼近这个上限而已 由此可见 特征工程在机器学习中占有相当重要的地位 在实际应用当中 可以说特征工程是机器学习成功的关键 那特征工程是什么 特征工程是利用数据领域的相关知识来创建能够使机器学习算
  • Xray 漏洞扫描工具使用方法

    本文仅供学习参考 严禁在未经网站管理员允许的情况下对网站进行扫描 本文仅供学习参考 严禁在未经网站管理员允许的情况下对网站进行扫描 本文仅供学习参考 严禁在未经网站管理员允许的情况下对网站进行扫描 滥用漏扫工具 违法国家安全法后果自负 申明
  • python中的tkinter包的使用--Menubar菜单窗口

    下面的例子讲如何制作简单的菜单 初始化窗口 点击子菜单New 继续点击其他子菜单 代码 import tkinter as tk window tk Tk window title my window window geometry 200
  • abaqus运行子程序出现无法打开输入文件msmpi.lib的解决办法

    好不容易搞定了abaqus Visual studio2013 Fortran 2013的关联 进行尝试时利用子程序进行计算时 出现了以下错误 End Compiling Abaqus Standard User Subroutines B
  • 【无标题】python:将一个表格的一行或几行数据通过双击或按键方式转移到另外一个表格中去

    import sys from PyQt5 QtWidgets import from move import QtWidgets QMainWindow 继承该类方法 class Utest window QtWidgets QMainW
  • Windows下安装使用mysqldumpslow

    首先需要安装Perl 在windows下安装Perl 安装过程很简单 从官网 http strawberryperl com 下载windows安装包 安装好之后 测试perl v 如果能显示版本号 表示安装成功 mysqldumpslow
  • muduo3学习笔记——Timestamp.{h,cc}

    首先代码中的一些要点 1 Timestamp类继承自boost less than comparable 模板类 只要实现 lt 即可自动实现 gt lt gt 2 使用到了BOOST STATIC ASSERT 编译时断言 3 gmtim
  • 查验身份证 C语言

    碎碎念念 这道题很坑 注意这里的权重是直接乘上去 一开始我乘的是百分之几 死活不知道哪里有问题 后来把一百乘上去 就对了 代码 include
  • 计算机网络第一章课后习题

    1 14 计算机网络有哪些常用的性能指标 速率 带宽 吞吐量 时延 时延带宽积 往返时间RTT 利用率 1 17 收发两端之间的传输距离为1000km 信号在媒体上的传播速率为2 x 10 8 m s 试计算以下两种情况的发送时延和传播时延
  • Android 开发中 Kotlin Coroutines 如何优雅地处理异常

    一 尽量少用 GlobalScope GlobalScope 是 CoroutineScope 的实现类 我们以前使用过的 launch async 函数都是 CoroutineScope 的扩展函数 GlobalScope 没有绑定任何
  • 为 Excalidraw 添加手写中文字体

    Excalidraw目前支持手写英文 不支持手写中文 导致画出的图不好看 那么 如何添加手写中文字体呢 准备一个手写中文字体 然后上传到一个存储位置 可以访问的地方 或者本地静态服务器 获取到一个可访问连接 可以放到云服务器上 可以放到Gi
  • 解决maven clean 报错 Process terminated

    设置一下maven
  • QT表格控件实例(Table Widget 、Table View)

    欢迎小伙伴的点评 相互学习 博主 本着开源的精神交流Qt开发的经验 将持续更新续章 为社区贡献博主自身的开源精神 文章目录 前言 一 图示实例 二 列表常用成员解析 三 代码实例解析 UI设计如下 mainwindow h main cpp
  • 使用Rest API设计简单的博客网站

    博客根地址 https mygithub com 对于每个用户自己的博客网站都在这个根地址后加url信息 使用Rest API进行设计 在这里设计如下API https mygithub com username articles http
  • python解释器的安装与配置

    目录 1 Python解释器安装配置 2 Python环境变量设置 3 Python解释器多个版本共存 1 Python解释器安装配置 python解释器是能够解释执行 Python代码的程序 它可以解析和执行 Python 程序 首先前往
  • c#之构造函数和析构函数

    如有错误 欢迎指正
  • 【五一创作】某头条参数破解并实现界面化搭建

    某条参数破解并实现界面化搭建 前言 效果展示 难点 参数逆向破解 signature ac signature s v web id 界面化实现 总结 前言 趁着日常闲余时间 想着搞一搞某条的反爬 练练手 想到自己很久没开发过前端界面了 有
  • JAVA线程 -- wait notify notifyAll

    在通常的代码中实现线程互斥用的较多的是synchronized synchronized this 与synchronized static Object 的区别 synchronized就是针对内存区块申请内存锁 this关键字代表类的一