我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java线程通信之wait-notify通信方式详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java线程通信之wait-notify通信方式详解

问题:

1.线程 wait()方法使用有什么前提?

2. 多线程之间如何进行通信?

3. Java 中 notify 和 notifyAll 有什么区别?

4. 为什么 wait/notify/notifyAll 这些方法不在 thread 类里面?

5. 为什么 wait 和 notify 方法要在同步块中调用?

6. notify()和 notifyAll()有什么区别?

1. 线程通信的定义

线程是操作系统调度的最小单位,有自己的栈空间,可以按照既定的代码逐步执行,但是如果每个线程间都孤立地运行,就会造资源浪费。所以在现实中,如果需要多个线程按照指定的规则共同完成一个任务,那么这些线程之间就需要互相协调,这个过程被称为线程的通信。

线程的通信可以被定义为:当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以避免无效的资源争夺。

线程间通信的方式可以有很多种:等待-通知、共享内存、管道流。“等待-通知”通信方式是Java中使用普遍的线程间通信方式,其经典的案例是“生产者-消费者”模式。

2. 为什么需要wait-notify?

场景:有几个小孩都想进入房间内使用算盘(CPU)进行计算,老王(操作系统)就使用了一把锁(synchronized)让同一时间只有一个小孩能进入房间使用算盘,于是他们排队进入房间。

(1) 小南最先获取到了锁,进入到房间内,但是由于条件不满足(没烟干不了活),小南不能继续进行计算 ,但小南如果一直占用着锁,其它人就得一直阻塞,效率太低。

在这里插入图片描述

(2) 于是老王单开了一间休息室(调用 wait 方法),让小南到休息室(WaitSet)等着去了,这时锁释放开, 其它人可以由老王随机安排进屋

(3) 直到小M将烟送来,大叫一声 [ 你的烟到了 ] (调用 notify 方法)

在这里插入图片描述

(4) 小南于是可以离开休息室,重新进入竞争锁的队列

在这里插入图片描述

java语言中“等待-通知”方式的线程间通信使用对象的wait()、notify()两类方法来实现。每个java对象都有wait()、notify()两类实例方法,并且wait()、notify()方法和对象的监视器是紧密相关的。

wait()、notify()两类方法在数量上不止两个。wait()、notify()两类方法不属于Thread类,而是属于java对象实例。

3. wait方法和notify方法

java对象中的wait()、notify()两类方法就如同信号开关,用于等待方和通知方之间的交互。

1、对象的wait()方法

对象的wait()方法的主要作用是让当前线程阻塞并等待被唤醒。wait()方法与对象监视器紧密相关,使用wait()方法时一定要放在同步块中。wait()方法的调用方法如下:

public class Main {
    static final Object lock = new Object();
    public static void method1() throws InterruptedException {
        synchronized( lock ) {
            lock.wait();
        }
    }
}

Object类中的wait()方法有三个版本:

(1) void wait():当前线程调用了同步对象lockwait()实例方法后,将导致当前的线程等待,当前线程进入lock的监视器WaitSet,等待被其他线程唤醒;

(2) void wait(long timeout):限时等待。导致当前的线程等待,等待被其他线程唤醒,或者指定的时间timeout用完,线程不再等待;

(3) void wait(long timeout,int nanos):高精度限时等待,其主要作用是更精确地控制等待时间。参数nanos是一个附加的纳秒级别的等待时间;

2、对象的notify()方法

对象的notify()方法的主要作用是唤醒在等待的线程。notify()方法与对象监视器紧密相关,调用notify()方法时也需要放在同步块中。notify()方法的调用方法如下:

public class Main {
    static final Object lock = new Object();
    public static void method1() throws InterruptedException {
        synchronized( lock ) {
            lock.notify();
        }
    }
}

notify()方法有两个版本:

(1)void notify()lock.notify()调用后,唤醒lock监视器等待集中的第一条等待线程;被唤醒的线程进入EntryList,其状态从WAITING变成BLOCKED。

(2) void notifyAll()lock.notifyAll()被调用后,唤醒lock监视器等待集中的全部等待线程,所有被唤醒的线程进入EntryList,线程状态从WAITING变成BLOCKED。

小结:

obj.wait():让进入Object监视器的线程到waitset等待

obj.notify():在Object上正在waitset等待的线程中挑一个唤醒

obj.notifyAll():让在Object上正在waitset等待的线程全部唤醒

4. wait方法和notify方法的原理

对象的wait()方法的核心原理大致如下:

(1) 当线程调用了lock(某个同步锁对象)的wait()方法后,jvm会将当前线程加入lock监视器的WaitSet(等待集),等待被其他线程唤醒。

(2) 当前线程会释放lock对象监视器的Owner权利,让其他线程可以抢夺lock对象的监视器。

(3) 让当前线程等待,其状态变成WAITING。在线程调用了同步对象lock的wait()方法之后,同步对象lock的监视器内部状态大致如图2-15所示。

对象的notify()或者notifyAll()​​​​​​​​​​​​​​方法的原理大致如下:

(1) 当线程调用了lock(某个同步锁对象)的notify()方法后,jvm会唤醒lock监视器WaitSet中的第一条等待线程。

(2) 当线程调用了locknotifyAll()方法后,jvm会唤醒lock监视器WaitSet中的所有等待线程。

(3) 等待线程被唤醒后,会从监视器的WaitSet移动到EntryList,线程具备了排队抢夺监视器Owner权利的资格,其状态从WAITING变成BLOCKED。

(4) EntryList中的线程抢夺到监视器的Owner权利之后,线程的状态从BLOCKED变成Runnable,具备重新执行的资格。

在这里插入图片描述

(1) Owner 线程发现条件不满足,调用wait 方法,即可进入WaitSet,变为 WAITING 状态 ;

(2) BLOCKED 和WAITING 的线程都处于阻塞状态,不占用CPU时间片 ;

(3) BLOCKED:线程会在Owner 线程释放锁时唤醒 ;

(4) WAITING :线程会在Owner 线程调用notify 或 notifyAll时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入 EntryList 重新竞争;

5. wait方法和notify方法示例

1、进入Object监视器的线程才能调用wait()方法

小南并不能直接进入WaitSet休息室,而是获取锁进入房间后才能进入休息室,没有锁的话小南没法进入房间,更没法进入休息室。他进入休息室后就会释放锁,让其他线程竞争锁进入房间。

在这里插入图片描述

public class Main {
    static final Object lock = new Object();
    public static void main(String[] args) {
        synchronized (lock){
            try {
                // 只有成为了monitor对象的owner,即获得了对象锁之后,才有资格进入waitset等待
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2、进入Object监视器的线程才能调用notify()方法

小M此时获取到了锁,进入了房间,并唤醒了在休息室中等待的小王,小M如果获取到锁进行房间时没有办法唤醒在休息室等待的小王的,因为此时小M在门外,小王根本听不到。

在这里插入图片描述

使用notify()唤醒等待区的一个线程:

public class Main {
    static final Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            System.out.println("t1线程开始执行...");
            synchronized (lock){
                try {
                    // 让t1线程在lock锁的waitset中等待
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 被唤醒后,继续执行
                System.out.println("线程t1被唤醒...");
            }
        },"t1");
        t1.start();
        Thread t2 = new Thread(()->{
            System.out.println("t2线程开始执行...");
            synchronized (lock){
                try {
                    // 让t2线程在lock锁的waitset中等待
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 被唤醒后,继续执行
            System.out.println("线程t2被唤醒...");
        },"t2");
        t2.start();
        Thread.sleep(2000);
        System.out.println("唤醒lock锁上等待的线程...");
        synchronized (lock){
            // 主线程拿到锁后,唤醒正在休息室中等待的某一个线程
            lock.notify();
        }
    }
}

执行结果:

t1线程开始执行...
t2线程开始执行...
唤醒lock锁上等待的线程...
线程t1被唤醒...

使用notifyAll()唤醒等待区所有的线程:

public class Main {
    static final Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            System.out.println("t1线程开始执行...");
            synchronized (lock){
                try {
                    // 让t1线程在lock锁的waitset中等待
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 被唤醒后,继续执行
                System.out.println("线程t1被唤醒...");
            }
        },"t1");

        t1.start();
        Thread t2 = new Thread(()->{
            System.out.println("t2线程开始执行...");
            synchronized (lock){
                try {
                    // 让t2线程在lock锁的waitset中等待
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 被唤醒后,继续执行
            System.out.println("线程t2被唤醒...");
        },"t2");
        t2.start();

        Thread.sleep(2000);

        System.out.println("唤醒lock锁上等待的线程...");
        synchronized (lock){
            // 主线程拿到锁后,唤醒正在休息室中等待的所有线程
            lock.notifyAll();
        }
    }
}

执行结果:

t1线程开始执行...
t2线程开始执行...
唤醒lock锁上等待的线程...
线程t2被唤醒...
线程t1被唤醒...

6. 为什么 wait 和 notify 方法要在同步块中调用?

在调用同步对象的wait()和notify()系列方法时,“当前线程”必须拥有该对象的同步锁,也就是说,wait()和notify()系列方法需要在同步块中使用,否则JVM会抛出类似如下的异常:

在这里插入图片描述

为什么wait和notify不在synchronized同步块的内部使用会抛出异常呢?这需要从wait()和notify()方法的原理说起。

wait()方法的原理:

首先,JVM会释放当前线程的对象锁监视器的Owner资格;其次,JVM会将当前线程移入监视器的WaitSet队列,而这些操作都和对象锁监视器是相关的。所以,wait()方法必须在synchronized同步块的内部调用。在当前线程执行wait()方法前,必须通过synchronized()方法成为对象锁的监视器的Owner。

notify()方法的原理:

JVM从对象锁的监视器的WaitSet队列移动一个线程到其EntryList队列,这些操作都与对象锁的监视器有关。所以,notify()方法也必须在synchronized同步块的内部调用。在执行notify()方法前,当前线程也必须通过synchronized()方法成为对象锁的监视器的Owner。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容!  

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java线程通信之wait-notify通信方式详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java线程通信中wait-notify通信的方式是什么

这篇文章主要介绍“Java线程通信中wait-notify通信的方式是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java线程通信中wait-notify通信的方式是什么”文章能帮助大家解决问
2023-06-29

浅谈Java线程间通信之wait/notify

Java中的wait/notify/notifyAll可用来实现线程间通信,是Object类的方法,这三个方法都是native方法,是平台相关的,常用来实现生产者/消费者模式。先来我们来看下相关定义:wait() :调用该方法的线程进入WA
2023-05-31

Java如何使用wait/notify实现线程间通信

本文小编为大家详细介绍“Java如何使用wait/notify实现线程间通信”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java如何使用wait/notify实现线程间通信”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习
2023-07-04

Java怎么使用wait或notify实现线程间通信

这篇文章主要介绍“Java怎么使用wait或notify实现线程间通信”,在日常操作中,相信很多人在Java怎么使用wait或notify实现线程间通信问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java怎
2023-07-04

Java中怎么使用wait和notify实现线程间的通信

这篇“Java中怎么使用wait和notify实现线程间的通信”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Java中怎么使
2023-06-30

Java通过wait()和notifyAll()方法实现线程间通信

本文实例为大家分享了Java实现线程间通信的具体代码,供大家参考,具体内容如下Java代码(使用了2个内部类):package Threads;import java.util.LinkedList;/** * Created by Fra
2023-05-31

Java多线程wait()和notify()方法详细图解

wait()和notify()是直接隶属于Object类,也就是说所有对象都拥有这一对方法,下面这篇文章主要给大家介绍了关于Java多线程wait()和notify()方法详细图解的相关资料,需要的朋友可以参考下
2022-11-13

java多线程编程之管道通信详解

上一章节讲了wait/notify通信,这一节我们来探讨使用管道进行通信。 java中提供了IO流使我们很方便的对数据进行操作,pipeStream是一种特殊的流,用于不同线程间直接传送数据。一个线程将数据发送到输出管道,另一个线程从输入管
2023-05-30

Java学习之线程同步与线程间通信详解

这篇文章主要为大家详细介绍了线程同步和线程之间的通信的相关知识,文中的示例代码讲解详细,对我们学习Java有一定的帮助,感兴趣的可以了解一下
2022-12-27

linux多线程编程详解教程(线程通过信号量实现通信代码)

线程分类 线程按照其调度者可以分为用户级线程和核心级线程两种。 (1)用户级线程 用户级线程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自行选择决定,在运行时不需要特定的内核支持。在这里,操作系统往往会提供一个用户空间的线
2022-06-04

Android中子线程和UI线程通信详解

Android中子线程和UI线程之间通信的详细解释 1.在多线程编程这块,我们经常要使用Handler,Thread和Runnable这三个类,那么他们之间的关系你是否弄清楚了呢下面详解一下。 2.首先在开发Android应用时必须遵守
2022-06-06

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录