这篇文章主要介绍了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多线程基础 线程的等待与唤醒(wait、notify、notifyAll),包括了Java多线程基础 线程的等待与唤醒(wait、notify、notifyAll)的使用技巧和注意事项,需要的朋友参考一下 本篇我们来研究一下 wait() notify() notifyAll() 。 DEMO1: wait() 与 notify() DEMO1 输出: 注意: 使用 wait
本文向大家介绍Java SPI机制原理及代码实例,包括了Java SPI机制原理及代码实例的使用技巧和注意事项,需要的朋友参考一下 SPI的全名为:Service Provider Interface,大多数开发人员可能不熟悉,因为这个是针对厂商或者插件的。在java.util.ServiceLoader的文档里有比较详细的介绍。 简单的总结下 Java SPI 机制的思想。我们系统里抽象的各个模
本文向大家介绍JavaScript事件冒泡机制原理实例解析,包括了JavaScript事件冒泡机制原理实例解析的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了JavaScript事件冒泡机制原理实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 DOM事件流(event flow )存在三个阶段:事件捕获阶段、处于目标阶段、事件冒
本文向大家介绍Java包装类的缓存机制原理实例详解,包括了Java包装类的缓存机制原理实例详解的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了Java包装类的缓存机制原理实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 java 包装类的缓存机制,是在Java 5中引入的一个有助于节省内存、提高性能的功能,只有在自动装箱时有效
本文向大家介绍Redis处理高并发机制原理及实例解析,包括了Redis处理高并发机制原理及实例解析的使用技巧和注意事项,需要的朋友参考一下 1.Redis是基于内存的,内存的读写速度非常快; 2.Redis是单线程的,省去了很多上下文切换线程的时间; 3.Redis使用多路复用技术,可以处理并发的连接。非阻塞IO 内部实现采用epoll,采用了epoll+自己实现的简单的事件框架。epoll中的读