博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
notify,notifyAll区别(生产者消费者案例)
阅读量:7083 次
发布时间:2019-06-28

本文共 4048 字,大约阅读时间需要 13 分钟。

本文通过wait(),notify(),notifyAll()模拟生产者-消费者例子,说明为什么使用notify()会发生死锁。

1.代码示例

1.1 生产者

package com.example.hxk.thread.demo;import java.util.List;import java.util.concurrent.TimeUnit;/** * @author Smith 2019/3/21 */public class Producer implements Runnable{    List
cache; public void put() throws InterruptedException { synchronized (cache) { while (cache.size() == 1) { try { cache.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } TimeUnit.SECONDS.sleep(1); cache.add(1); System.out.println(Thread.currentThread().getName() + "生产者生产了一条。"); cache.notify(); } } public Producer(List
cache) { this.cache = cache; } @Override public void run() { while (true) { try { put(); } catch (InterruptedException e) { e.printStackTrace(); } } }}复制代码

1.2 消费者

package com.example.hxk.thread.demo;import java.util.List;/** * @author Smith 2019/3/21 */public class Customer implements Runnable {    List
cache; public Customer(List
cache) { this.cache = cache; } private void custom() { synchronized (cache) { while (cache.size() == 0) { try { cache.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } cache.remove(0); System.out.println(Thread.currentThread().getName() + "消费者消费了一条。"); cache.notify(); } } @Override public void run() { while (true) { custom(); } }}复制代码

1.3 测试代码

1.3.1 一个生产者一个消费者

package com.example.hxk.thread.demo;import java.util.ArrayList;import java.util.List;/** * @author Smith 2019/3/21 */public class Test {    public static void main(String[] args) {        List
cache = new ArrayList<>(); new Thread(new Producer(cache), "P1").start(); new Thread(new Customer(cache), "C1").start(); }}复制代码

运行结果:

结论:

使用notify且一个生产者一个消费者的情况下,生产和消费有条不紊的运行,没有任何问题。

1.3.2 一个生产者两个消费者

package com.example.hxk.thread.demo;import java.util.ArrayList;import java.util.List;/** * @author Smith 2019/3/21 */public class Test {    public static void main(String[] args) {        List
cache = new ArrayList<>(); new Thread(new Producer(cache), "P1").start(); new Thread(new Customer(cache), "C1").start(); new Thread(new Customer(cache), "C2").start(); }}复制代码

运行结果:

结论:

使用notify且一个生产者两个消费者的情况下,生产了两次后,程序死锁了。

1.3.3 将Producer和Customer中的notify()换成notifyAll()

代码就不粘了。

运行结果

程序又恢复了正常。

2.notify()和notifyAll的区别

每个同步对象都有自己的锁池和等待池。

2.1 锁池和等待池

  • 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。
  • 等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁(因为wait()方法必须出现在synchronized中,这样自然在执行wait()方法之前线程A就已经拥有了该对象的锁),同时线程A就进入到了该对象的等待池中。如果另外的一个线程调用了相同对象的notifyAll()方法,那么处于该对象的等待池中的线程就会全部进入该对象的锁池中,准备争夺锁的拥有权。如果另外的一个线程调用了相同对象的notify()方法,那么仅仅有一个处于该对象的等待池中的线程(随机)会进入该对象的锁池.

2.2 notify()和notifyAll()的区别

  • 线程调用了wait()方法,便会释放锁,并进入等待池中,不会参与锁的竞争
  • 调用notify()后,等待池中的某个线程(只会有一个)会进入该对象的锁池中参与锁的竞争,若竞争成功,获得锁,竞争失败,继续留在锁池中等待下一次锁的竞争。
  • 调用notifyAll()后,等待池中的所有线程都会进入该对象的锁池中参与锁的竞争。

3.例子解析

知道了2的知识后,上面的三个例子也就好解释了。

1.3.1:因为只有一个生产者和消费者,所以等待池中始终只有一个线程,要么就是生产者唤醒消费者,要么消费者唤醒生产者,所以程序可以成功跑起来;

1.3.2:举个可能的例子

  1. 现在有三个线程,生产者P1, 消费者C1和C2.开始运行的时候,三个都在锁池中等待竞争,假设C1抢到锁了,C1执行时由于没有资源可以消费 调用wait()方法,释放锁并进入等待池。
  2. C2抢到了锁,开始消费,同理,C2也进入了等待池。现在锁池里面只剩下了P1.
  3. P1获得了锁,开始生产,生产完成后,P1开始调用notify()方法唤醒等待池中的C1或者C2,然后P1调用wait()方法释放锁,并进入了等待池。
  4. 假设唤醒的是C1,C1进入锁池并获得锁,消费后notify()方法唤醒了C2,C2进入锁池,C1进入等待池,现在锁池中只有C1。
  5. C1获得了锁,发现没有任何资源可以消费,wait()后释放了锁,进入了等待池,现在三个线程全都在等待池,锁池中没有任何线程。导致死锁!

1.3.3: notifyAll()后,不存在只唤醒同类线程的情况,故也就不会出现1.3.2死锁的情况。

引用

  • 作者:emailed 来源:CSDN 原文:

  • 作者:泡芙掠夺者 来源:简书 原文:

转载于:https://juejin.im/post/5c930141e51d450ad30e43d3

你可能感兴趣的文章
导出csv用excel打开后数字不用科学计数法显示(0123456显示123456)
查看>>
ssm框架,出现xxx不能加载,或者bean不能加载时的解决方案之一
查看>>
springmvc+mybatis多数据源配置,AOP注解动态切换数据源
查看>>
Centos 6.8 系统下安装RabbitMQ方法
查看>>
SQL Server不能启动
查看>>
Educational Codeforces Round 65 (Rated for Div. 2) C. News Distribution
查看>>
[转] 如何写好.babelrc?Babel的presets和plugins配置解析
查看>>
The JVM Architecture Explained
查看>>
输入框禁止表情
查看>>
最大乘积(大佬的代码)
查看>>
dagger android 学习(四):基于dagger2的mvp架构
查看>>
CentOs7 使用iptables防火墙开启关闭端口
查看>>
12.29.作业
查看>>
项目管理初探
查看>>
keras入门--Mnist手写体识别
查看>>
animation渐进实现点点点等待效果实例页面
查看>>
配置 ssh无密码登陆
查看>>
java读取和写入浏览器Cookies
查看>>
熟悉常用的HDFS操作
查看>>
SCM软件配置管理 (一)SVN 与 CVS
查看>>