Java 中的等待唤醒机制透彻讲解
线程的状态
首先了解一下什么是线程的状态,线程状态就是当线程被创建(new),并且启动(start)后,它不是一启动就进入了执行状态(run),也不是一直都处于执行状态。
这里说一下Java 的Thread类里面有一个State方法,这个方法里面涵盖了6种线程的状态,如下:
public enum State {
    // 尚未启动的线程的线程状态。
    NEW,
    // 可运行线程的线程状态。
    RUNNABLE,
    // 线程的线程状态被阻塞,等待监视器锁定。
    BLOCKED,
    // 等待线程的线程状态。
    WAITING,
    // 具有指定等待时间的等待线程的线程状态。
    TIMED_WAITING,
    // 终止线程的线程状态。
    TERMINATED;
}
导致这六种线程状态发生的条件
New -- 新建
线程刚被创建,不过还没有被启动(还没有调用start方法)
Runnable -- 可运行
处于可运行状态的线程正在Java虚拟机中执行,但是它可能正在等待来自操作系统(例如处理器)的其他资源。
Blocked -- 锁阻塞
当一个线程想获取一个对象锁,不过该对象锁被其它的线程持有时,该线程就会进入锁阻塞状态;当该线程持有锁的时候,该线程将会变成可运行的状态。
Waiting -- 无限等待
当一个线程在等待另一个线程执行一个(唤醒)动作时,该线程就会进入无限等待状态。进入这个状态后是不能自动唤醒的,要等待另一个线程调用notify()方法,或notifyall()方法才能够被唤醒。
Timed_Waiting -- 计时等待
类似于无限等待状态,有几个方法有超时参数,如:Thread.sleep、Object.wait方法。调用这些方法,进入计时等待状态。计时等待状态将会一直保持到超时期满或者接收到唤醒通知。
terminated -- 被终止
1、因为run方法的正常退出而死亡。
2、因为没有捕获的异常,终止了run方法而死亡。
等待唤醒案例切入
顾客要去饭店吃饭,自助下单,说明要吃什么,数量是多少。下完单以后,顾客就等待该饭店厨师做饭菜,也就是Waiting状态(无限等待状态)。
厨师收到下单信息,开始做饭菜,做好饭菜,把饭菜递到顾客桌面上,顾客看到饭菜已经来了(notify方法),就可以开吃了(等待唤醒机制)。
Java代码实现(线程之间的通信)
分析
创建一个顾客线程:下单,告知厨师要什么菜,菜的数量,调用wait方法,放弃CPU的执行,进入到无限等待状态(Waiting)
创建一个厨师线程:看到下单,花了3秒钟做饭菜,做好之后,调用notify方法,唤醒顾客吃饭了。
注意
顾客线程和厨师线程,必须使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行。
同步使用的锁对象必须保证唯一。
只有锁对象才能够调用Object.wait方法和Object.notify方法。
代码
public class Demo01WaitNotify {
    public static void main(String[] args) {
        // 创建锁对象(要保证锁唯一)
        Object object = new Object();
        // 创建一个顾客线程
        new Thread() {
            @Override
            public void run() {
                // 使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行。
                synchronized (object) {
                    // 顾客下单
                    System.out.println("我要一个西虹市炒番茄,一个马铃薯炒土豆,两碗米饭");
                    // 调用wait方法,放弃CPU的执行,进入到无限等待状态(Waiting)
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 唤醒之后(饭菜上来后),吃饭!!!真香。
                    System.out.println("我就是饿死,从这里跳下去,也不会吃你们一口饭。。。真香!!!!");
                }
            }
        }.start();
        // 创建一个厨师线程
        new Thread() {
            @Override
            public void run() {
                // 厨师收到下单请求,花三秒钟把饭菜做好
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行。
                synchronized (object) {
                    System.out.println("我的饭菜三秒钟做好了,你食唔食哦?");
                    // 做好之后,调用notify方法,唤醒顾客吃饭了。
                    object.notify();
                }
            }
        }.start();
    }
}
控制台输出:
我要一个西虹市炒番茄,一个马铃薯炒土豆,两碗米饭
我的饭菜三秒钟做好了,你食唔食哦?
我就是饿死,从这里跳下去,也不会吃你们一口饭。。。真香!!!!
上面的代码,存在线程间的通信,那什么又是线程间的通信呢?简单的说,就是多个线程在处理同一个资源,但是处理的动作(线程的任务)却不同。如上,厨师线程做饭菜,顾客线程吃饭菜。那为什么要进行线程间的通信呢?多个线程并发执行的时候,在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且希望它们有规律的执行的时候,那么多线程就之间就需要一些协调通信,来达到多线程共同操作一份数据。
对代码中通信的理解:
对又没有饭菜进行判断——
1、没有饭菜(False)。
2、顾客下单。
3、厨师做饭菜。
4、顾客线程等待。
5、厨师做好饭菜
6、修改饭菜的状态(True)
7、有饭菜,厨师线程提醒顾客线程吃饭菜。
8、厨师线程等待
9、吃完饭菜,修改饭菜的状态(False)
这就是顾客线程与厨师线程之间的通信。以此类推,其它Java程序中多线程的通信也是同样的道理。
Java 中的等待唤醒机制就解析到这里了,如果有什么不足、错误的地方,希望大佬们指正。
Java 中的等待唤醒机制透彻讲解的更多相关文章
- 多线程之Java中的等待唤醒机制
		
多线程的问题中的经典问题是生产者和消费者的问题,就是如何让线程有序的进行执行,获取CPU执行时间片的过程是随机的,如何能够让线程有序的进行,Java中提供了等待唤醒机制很好的解决了这个问题! 生产者消 ...
 - Java中的等待唤醒机制—至少50%的工程师还没掌握!
		
这是一篇走心的填坑笔记,自学Java的几年总是在不断学习新的技术,一路走来发现自己踩坑无数,而填上的坑却屈指可数.突然发现,有时候真的不是几年工作经验的问题,有些东西即使工作十年,没有用心去学习过也不 ...
 - java基础知识回顾之java Thread类学习(八)--java多线程通信等待唤醒机制经典应用(生产者消费者)
		
*java多线程--等待唤醒机制:经典的体现"生产者和消费者模型 *对于此模型,应该明确以下几点: *1.生产者仅仅在仓库未满的时候生产,仓库满了则停止生产. *2.消费者仅仅在有产品的时 ...
 - Java学习:等待唤醒机制
		
等待唤醒机制 线程的状态 NEW 至今尚未启动的线程处于这种状态 RUNNABLE 正在Java虚拟机中执行的线程处于这种状态 BLOCKED 受阻塞并等待某个监视器锁的线程处于这种状态 WA ...
 - java基础知识回顾之java Thread类学习(七)--java多线程通信等待唤醒机制(wait和notify,notifyAll)
		
1.wait和notify,notifyAll: wait和notify,notifyAll是Object类方法,因为等待和唤醒必须是同一个锁,不可以对不同锁中的线程进行唤醒,而锁可以是任意对象,所以 ...
 - java基础-多线程  等待唤醒机制
		
/** * @param args * 等待唤醒机制 */ public static void main(String[] args) { final Printer p = new ...
 - java锁在等待唤醒机制中作用
		
等待的线程放在线程池wait().notify().notifyall()都使用在同步中,因为要对持有监视器(锁)的线程操作.所以要使用在同步中,因为只有同步才具有锁. 为什么这些操作的线程的方法要定 ...
 - java多线程的等待唤醒机制及如何解决同步过程中的安全问题
		
/* class Person{ String name; String sex; boolean flag = true; public void setPerson(String name, St ...
 - Android(java)学习笔记71:生产者和消费者之等待唤醒机制
		
1. 首先我们根据梳理我们之前Android(java)学习笔记70中关于生产者和消费者程序思路: 2. 下面我们就要重点介绍这个等待唤醒机制: (1)第一步:还是先通过代码体现出等待唤醒机制 pac ...
 
随机推荐
- python模块之包
			
包:将解决一类问题的模块放在同一目录下就形成了一个包 为了更好的了解包,我们就模拟创建一个包 import os os.makedirs('glance/api') os.makedirs('glan ...
 - python基础七之集合
			
集合:可变的数据类型,他里面的元素必须是不可变的数据类型,无序,不重复. 增加 set1 = {'zxc', 'zxf'} set1.add('zxv') # 无序添加 set1.update('zx ...
 - css 百分比继承关系的探讨
			
引入:近日在回顾css基础的时候发现了一个有趣的问题,就是css在继承百分比类属性的时候是继承百分比后再根据父级宽高计算,还是继承父级根据百分比计算过后的绝对值.下面举一个简单的例子来描述这一个问题, ...
 - js基础-原型
			
1.定义:我们创建的函数都有一个prototype(原型)属性,该属性是一个对象, 原型模式声明中多了两个属性(自动生成). 构造函数: function Box(nam ...
 - grep简介
			
grep -ril "xxxxx" . :查看当前目录下所有文件是否包含指定字符,只输出符合条件的文件名 -r :递归查找 -i:忽略大小写 -l :只输出文件名
 - 基于koa2操作mysql封装例子
			
新建better-mysql.js const mysql = require('mysql'); const config = require('../config/sqlConfig.js') l ...
 - LuoguP3521 [POI2011]ROT-Tree Rotations
			
P3521 [POI2011]ROT-Tree Rotations 题目大意: 给一棵\((1≤n≤200000)\)个叶子的二叉树,可以交换每个点的左右子树,要求前序遍历叶子的逆序对最少. 我们发现 ...
 - 【NOIP模拟赛】【数学】完全平方数
			
问题描述 一个数如果是另一个整数的完全平方,那么我们就称这个数为完全平方数(Pefect Sqaure),也称平方数. 小A认为所有的平方数都是很perfect的~ 于是他给了小B一个任务:用任意个不 ...
 - JDBC 数据源
			
概述 JNDI 数据源配置的相关内容已经在 JNDI 资源文档中详细介绍过.但从 Tomcat 用户的反馈意见来看,有些配置的细节问题非常棘手. 针对常用的数据库,我们已经给 Tomcat 用户提供了 ...
 - 剑指Offer-62.数据流中的中位数(C++/Java)
			
题目: 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值.我们使 ...