wait
,notify
和notifyAll
,这些在多线程中被常常用到的保留关键字,在实际开发的时候不少时候却并无被你们重视,而本文则是对这些关键字的使用进行描述。java
<!-- more -->git
在java中,每一个对象都有两个池,锁池(monitor)和等待池(waitset),每一个对象又都有
wait
、notify
、notifyAll
方法,使用它们能够实现线程之间的通讯,只是平时用的较少。算法
notify
或notifyAll
将它唤醒锁池: 假设T1线程
已经拥有了某个对象(注意:不是类)的锁
,而其它的线程想要调用该对象的synchronized方法(或者synchronized块)
,因为这些线程在进入对象的synchronized方法
以前都须要先得到该对象的锁的拥有权,可是该对象的锁目前正被T1线程
拥有,因此这些线程就进入了该对象的锁池中。微信
等待池: 假设T1线程
调用了某个对象的wait()
方法,T1线程
就会释放该对象的锁(由于wait()方法必须出如今synchronized中,这样天然在执行wait()方法以前T1线程就已经拥有了该对象的锁)
,同时T1线程
进入到了该对象的等待池中。若是有其它线程调用了相同对象的notifyAll()
方法,那么处于该对象的等待池中的线程就会所有进入该对象的锁池中,重新争夺锁的拥有权。若是另外的一个线程调用了相同对象的notify()方法,那么仅仅有一个处于该对象的等待池中的线程(随机)会进入该对象的锁池.多线程
对象(注意:不是类)的锁
。wait
和 notify
,而不是在 If 语句中synchronized
的函数或对象里使用wait、notify和notifyAll
,否则Java虚拟机会生成 IllegalMonitorStateException
。private static int i = 0; static void product() {//生产者 System.out.println("P->" + (++i)); } static void consumer() {//消费者 System.out.println("C->" + i); } public static void main(String[] args) { new Thread(() -> { while (true) { product(); } }).start(); new Thread(() -> { while (true) { consumer(); } }).start(); } ////////////////////////日志//////////////////////// //P->1 //P->2 //P->3 //P->4 //C->1 ////////////////////////日志////////////////////////
分析: 从日志中能够看到数据会出现屡次生产或屡次消费的问题,由于在线程执行过程当中,两个线程缺乏协做关系,都是各干各的,T1线程
只管生产数据,无论T2线程
是否处理了。函数
private static int i = 0; private static boolean isProduction = true; static void product() {//生产者 if (isProduction) { System.out.println("P->" + (++i)); isProduction = false; } } static void consumer() {//消费者 if (!isProduction) { System.out.println("C->" + i); isProduction = true; } }
分析: 这种状况下咱们经过维护一个变量的方式,通知对方,可是效率及其差,线程频繁请求与判断大大的浪费了系统资源,虽然知足了当前要求,但并不是是可选方案...oop
上文已经介绍了使用wait
和notify
的前提了,接下来看案例spa
private final static byte[] LOCK = new byte[0];//定义一个锁对象 private static boolean isProduction = true;//消息投递 private static int i = 0;//生产的消息 static void product() { synchronized (LOCK) {// 必须是在 synchronized中 使用 wait/notify/notifyAll try { if (isProduction) {//若是标示位为生产状态,则继续生产 System.out.println("P->" + (++i)); isProduction = false; LOCK.notify();//消费者能够消费了 } else { LOCK.wait();//说明生产出来的数据还未被消费掉 } } catch (InterruptedException e) { e.printStackTrace(); } } } static void consumer() { try { synchronized (LOCK) { if (isProduction) {//若是当前还在生产,那么就暂停消费者线程 LOCK.wait(); } else { System.out.println("C->" + i); isProduction = true; LOCK.notify();//通知我已经消费完毕了 } } } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { new Thread(() -> { while (true) { product(); } }).start(); new Thread(() -> { while (true) { consumer(); } }).start(); } ////////////////////////日志//////////////////////// //P->1 //C->1 //P->2 //C->2 //..... //P->354217 //C->354217 ////////////////////////日志////////////////////////
分析: 一切都是那么完美,在T1线程
中,调用LOCK.wait()
将当前线程移入等待池中,并交出执行权,锁池中的其余线程去竞争并取得锁的使用权(T2线程获取
),当T2线程
消费完毕后,调用LOCK.notify()
方法通知当前对象锁等待池中的其中一个线程(由于这里notify是基于JVM算法而定
,由于咱们只有两个线程,因此T1线程
会接收到T2线程
发出的通知,从而继续生产数据。线程
问题: 虽然一对一没有问题,但假设多个生产者多个消费者的状况下怎么办呢?日志
public static void main(String[] args) { Stream.of("P1", "P2", "P3", "P4").forEach(name -> new Thread(() -> { while (true) { product(); } }, name).start()); Stream.of("C1", "C2").forEach(name -> new Thread(() -> { while (true) { consumer(); } }, name).start()); } ////////////////////////日志//////////////////////// //P2 -> 1 //C2 -> 1 //P2 -> 2 //C1 -> 2 //P3 -> 3 ////////////////////////日志////////////////////////
分析: 竟然不执行了,借助前面说过的 死锁分析知识,咱们看看是否是发生死锁了
结果代表,虽然没有Found one deadlock...
字眼,可是能够看到有个线程都被wait
住了,没有被释放,因此致使咱们当前没法继续生产消费
LOCK.notifyAll();//通知全部线程,我已经消费完毕了,大家继续生产
分析: 这里只修改了一句代码,就是将consumer方法
中的notify -> notifyAll
,由通知单个线程变成通知全部在等待池
中的线程
P1 -> 1 C1 -> 1 P2 -> 2 C2 -> 2 ... P3 -> 42894 C1 -> 42894 ... P1 -> 42898 C1 -> 42898
微信公众号:battcn
(欢迎调戏)
喜大普奔,迎来了十一国庆节....