三沣开发知识 购物 网址 小说 美女秀 游戏 电影下载 图说天下 生肖星座 新闻 笑话 | IT开发 软件下载 手机论坛 三沣软件 360图书馆
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
vbs/VBScript DOS/BAT hta htc python perl 游戏相关 VBA 远程脚本 ColdFusion ruby专题
autoit seraphzone PowerShell linux shell Lua Golang Erlang 其它教程 CSS/HTML/Xhtml
html5 CSS XML/XSLT Dreamweaver教程 经验交流 开发者乐园 Android开发资料
站长资讯 .NET新手 ASP.NET C# WinForm Silverlight WCF CLR WPF XNA VisualStudio ASP.NET-MVC .NET控件开发 EntityFramework WinRT-Metro Java C++ PHP Delphi Python Ruby C语言 Erlang Go Swift Scala R语言 Verilog 其它语言 架构设计 面向对象 设计模式 领域驱动 Html-Css JavaScript jQuery HTML5 SharePoint GIS技术 SAP OracleERP DynamicsCRM K2 BPM 信息安全 企业信息 Android开发 iOS开发 WindowsPhone WindowsMobile 其他手机 敏捷开发 项目管理 软件工程 SQLServer Oracle MySQL NoSQL 其它数据库 Windows7 WindowsServer Linux
  IT知识库 -> Java -> Java线程间通信之wait/notify -> 正文阅读
 

[Java]Java线程间通信之wait/notify

Java线程间通信之wait/notify   Java中的wait/notify/notifyAll可用来实现线程间通信,是Object类的方法,这三个方法都是native方法,是平台相关的,常用来实现生产者/消费者模式。先来我们来看下相关定义:
    wait() :调用该方法的线程进入WATTING状态,只有等待另外线程的通知或中断才会返回,调用wait()方法后,会释放对象的锁。
    wait(long):超时等待最多long毫秒,如果没有通知就超时返回。
    notify() : 通知一个在对象上等待的线程,使其从wait()方法返回,而返回的前提是该线程获取到了对象的锁。
    notifyAll():通知所有等待在该对象上的线程。
一个小例子
  我们来模拟个简单的例子来说明,我们楼下有个小小的饺子馆,生意火爆,店里有一个厨师,一个服务员,为避免厨师每做好一份,服务员端出去一份,效率太低且浪费体力。现假设厨师每做好10份,服务员就用一个大木盘子端给客户,每天卖够100份就打烊收工,厨师服务员各自回家休息。
  思考一下,要实现该功能,如果不使用等待/通知机制,那么最直接的方式可能就是,服务员隔一段时间去厨房看看,满10份就用盘子端出去。这种方式有两个很大的弊病:
  1.如果服务员去厨房看的太勤快,服务员太累了,这样还不如每做一碗就端一碗给客人,大木盘子的作用就体现不出来了。具体表现在实现代码层面就是:需要不断的循环,浪费处理器资源。
  2.如果服务员隔很久才去厨房看一下,就无法确保及时性了,可能厨师早都做够10份了,服务员却没观察到。
  针对上面这个例子,使用等待/通知机制就合理的多了,厨师每做够10份,就喊一声“饺子好了,可以端走啦”。服务员收到通知,就去厨房将饺子端给客人;厨师还没做够,即还没收到厨师的通知,就可以稍微休息下,但也得竖起耳朵等候厨师的通知。 
 


 1 package ConcurrentTest;
 2 
 3 import thread.BlockQueue;
 4 
 5 /**
 6  * Created by chengxiao on 2017/6/17.
 7  */
 8 public class JiaoziDemo {
 9     //创建个共享对象做监视器用
10     private static Object obj = new Object();
11     //大木盘子,一盘最多可盛10份饺子,厨师做满10份,服务员就可以端出去了。
12     private static Integer platter = 0;
13     //卖出的饺子总量,卖够100份就打烊收工
14     private static Integer count = 0;
15 
16     /**
17      * 厨师
18      */
19     static class Cook implements Runnable{
20         @Override
21         public void run() {
22             while(count<100){
23                 synchronized (obj){
24                     while (platter<10){
25                         platter++;
26                     }
27                     //通知服务员饺子好了,可以端走了
28                     obj.notify();
29                     System.out.println(Thread.currentThread().getName()+"--饺子好啦,厨师休息会儿");
30                 }
31                 try {
32                     //线程睡一会,帮助服务员线程抢到对象锁
33                     Thread.sleep(100);
34                 } catch (InterruptedException e) {
35                     e.printStackTrace();
36                 }
37             }
38             System.out.println(Thread.currentThread().getName()+"--打烊收工,厨师回家");
39         }
40     }
41 
42     /**
43      * 服务员
44      */
45     static class Waiter implements Runnable{
46         @Override
47         public void run() {
48             while(count<100){
49                 synchronized (obj){
50                     //厨师做够10份了,就可以端出去了
51                     while(platter < 10){
52                         try {
53                             System.out.println(Thread.currentThread().getName()+"--饺子还没好,等待厨师通知...");
54                             obj.wait();
55                             BlockQueue
56                         } catch (InterruptedException e) {
57                             e.printStackTrace();
58                         }
59                     }
60                     //饺子端给客人了,盘子清空
61                     platter-=10;
62                     //又卖出去10份。
63                     count+=10;
64                     System.out.println(Thread.currentThread().getName()+"--服务员把饺子端给客人了");
65                 }
66             }
67             System.out.println(Thread.currentThread().getName()+"--打烊收工,服务员回家");
68 
69         }
70     }
71     public static void main(String []args){
72         Thread cookThread = new Thread(new Cook(),"cookThread");
73         Thread waiterThread = new Thread(new Waiter(),"waiterThread");
74         cookThread.start();
75         waiterThread.start();
76     }
77 }

一个小例子
运行结果


cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--打烊收工,服务员回家
cookThread--打烊收工,厨师回家

运行结果
 运行机制
借用《并发编程的艺术》中的一张图来了解下wait/notify的运行机制

可能有人会对所谓监视器(monitor),对象锁(lock)不甚了解,在此简单解释下:
  jvm为每一个对象和类都关联一个锁,锁住了一个对象,就是获得了对象相关联的监视器。
  只有获取到对象锁,才能拿到监视器,如果获取锁失败了,那么线程就会进入阻塞队列中;如果成功拿到对象锁,也可以使用wait()方法,在监视器上等待,此时会释放锁,并进入等地队列中。
  关于锁和监视器的区别,园子里有个哥们的文章写得很详细透彻,在此引用一下,有兴趣的童鞋可以了解一下锁和监视器之间的区别 - Java并发
根据上面的图我们来理一下具体的过程
   1.首先,waitThread获取对象锁,然后调用wait()方法,此时,wait线程会放弃对象锁,同时进入对象的等待队列WaitQueue中;
   2.notifyThread线程抢占到对象锁,执行一些操作后,调用notify()方法,此时会将等待线程waitThread从等待队列WaitQueue中移到同步队列SynchronizedQueue中,waitThread由waitting状态变为blocked状态。需要注意的时,notifyThread此时并不会立即释放锁,它继续运行,把自己剩余的事儿干完之后才会释放锁;
  3.waitThread再次获取到对象锁,从wait()方法返回继续执行后续的操作;
  4.一个基于等待/通知机制的线程间通信的过程结束。
至于notifyAll则是在第二步中将等待队列中的所有线程移到同步队列中去。
避免踩坑
  在使用wait/notify/notifyAll时有一些特别留意的,在此再总结一下:
    1.一定在synchronized中使用wait()/notify()/notifyAll(),也就是说一定要先获取锁,这个前面我们讲过,因为只有加锁后,才能获得监视器。否则jvm也会抛出IllegalMonitorStateException异常。
    2.使用wait()时,判断线程是否进入wait状态的条件一定要使用while而不要使用if,因为等待线程可能会被错误地唤醒,所以应该使用while循环在等待前等待后都检查唤醒条件是否被满足,保证安全性。
    3.notify()或notifyAll()方法调用后,线程不会立即释放锁。调用只会将wait中的线程从等待队列移到同步队列,也就是线程状态从waitting变为blocked;
    4.从wait()方法返回的前提是线程重新获得了调用对象的锁。
后记
  关于wait/notify的相关内容就介绍到此,在实际使用中,要特别留意上文中提到的几点,不过一般情况下,我们直接使用wait/notify/notifyAll去完成线程间通信,生产者/消费者模型的机会不多,因为Java并发包中已经提供了很多优秀精妙的工具,像各种BlockingQueue等等,后面有机会也会详细介绍的。
  共勉
  Java 最新文章
Java入门——(2)面对对象(上)
Java虚拟机15:再谈四种引用状态
GITLAB email不发送腾讯企业邮箱解决方案
15套java架构师、集群、高可用、高可扩展、
spring集成JPA的三种方法配置
JAVA入门[21]
IO流常用方法,流的用法,代码例子
Java加密base64encode
Tachyon框架的Worker心跳及Master高可用性分
springMVC 接收数组参数,mybatis 接收数组
上一篇文章      下一篇文章      查看所有文章
加:2017-06-18 11:58:38  更:2017-06-18 11:58:44 
 
技术频道: 站长资讯 .NET新手区 ASP.NET C# WinForm Silverlight WCF CLR WPF XNA Visual Studio ASP.NET MVC .NET控件开发 Entity Framework WinRT/Metro Java C++ PHP Delphi Python Ruby C语言 Erlang Go Swift Scala R语言 Verilog 其它语言 架构设计 面向对象 设计模式 领域驱动设计 Html/Css JavaScript jQuery HTML5 SharePoint GIS技术 SAP Oracle ERP Dynamics CRM K2 BPM 信息安全 企业信息化其他 Android开发 iOS开发 Windows Phone Windows Mobile 其他手机开发 敏捷开发 项目与团队管理 软件工程其他 SQL Server Oracle MySQL NoSQL 其它数据库 Windows 7 Windows Server Linux
脚本语言: vbs/VBScript DOS/BAT hta htc python perl 游戏相关 VBA 远程脚本 ColdFusion ruby专题 autoit seraphzone PowerShell linux shell Lua Golang Erlang 其它教程
网站开发: CSS/HTML/Xhtml html5 CSS XML/XSLT Dreamweaver教程 经验交流 开发者乐园 Android开发资料
360图书馆 母婴/育儿 软件开发资料 网页快照 文字转语音 购物精选 软件 美食菜谱 新闻中心 电影下载 小游戏 Chinese Culture
生肖星座解梦 三沣玩客 拍拍 视频 开发 Android开发 站长 古典小说 网文精选 搜图网 天下美图 中国文化英文 多播视频
2017-6-29 11:47:02
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT知识库