java多线程15 :wait()和notify() 的生产者/消费者模式

在这一章已经实现了  wait/notify 生产消费模型


利用await()/signal()实现生产者和消费者模型

一样,先定义一个缓冲区:

public class ValueObject
{
public static String value = "";
}

换种写法,生产和消费方法放在一个类里面:

public class ThreadDomain41 extends ReentrantLock
{
private Condition condition = newCondition(); public void set()
{
try
{
lock();
while (!"".equals(ValueObject.value))
condition.await();
ValueObject.value = "123";
System.out.println(Thread.currentThread().getName() + "生产了value, value的当前值是" + ValueObject.value);
condition.signal();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
unlock();
}
} public void get()
{
try
{
lock();
while ("".equals(ValueObject.value))
condition.await();
ValueObject.value = "";
System.out.println(Thread.currentThread().getName() + "消费了value, value的当前值是" + ValueObject.value);
condition.signal();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
unlock();
}
}
}

同样的,开两个线程,一个线程调用set()方法生产,另一个线程调用get()方法消费:

public static void main(String[] args)
{
final ThreadDomain41 td = new ThreadDomain41();
Runnable producerRunnable = new Runnable()
{
public void run()
{
for (int i = 0; i < Integer.MAX_VALUE; i++)
td.set();
}
};
Runnable customerRunnable = new Runnable()
{
public void run()
{
for (int i = 0; i < Integer.MAX_VALUE; i++)
td.get();
}
};
Thread ProducerThread = new Thread(producerRunnable);
ProducerThread.setName("Producer");
Thread ConsumerThread = new Thread(customerRunnable);
ConsumerThread.setName("Consumer");
ProducerThread.start();
ConsumerThread.start();
}

看一下运行结果:

...
Producer生产了value, value的当前值是123
Consumer消费了value, value的当前值是
Producer生产了value, value的当前值是123
Consumer消费了value, value的当前值是
Producer生产了value, value的当前值是123
Consumer消费了value, value的当前值是
...

和wait()/notify()机制的实现效果一样,同样符合生产者/消费者模型

小心假死

生产者/消费者模型最终达到的目的是平衡生产者和消费者的处理能力,达到这个目的的过程中,并不要求只有一个生产者和一个消费者。可以多个生产者对应多个消费者,可以一个生产者对应一个消费者,可以多个生产者对应一个消费者。

假死就发生在上面三种场景下。理论分析就能说明问题,所以就不写代码了。代码要写也很简单,上面的两个例子随便修改一个,开一个生产者线程/多个消费者线程、开多个生产者线程/消费者线程、开多个生产者线程/多个消费者线程都可以。假死指的是全部线程都进入了WAITING状态,那么程序就不再执行任何业务功能了,整个项目呈现停滞状态。

比方说有生产者A和生产者B,缓冲区由于空了,消费者处于WAITING。生产者B处于WAITING,生产者A被消费者通知生产,生产者A生产出来的产品本应该通知消费者,结果通知了生产者B,生产者B被唤醒,发现缓冲区满了,于是继续WAITING。至此,两个生产者线程处于WAITING,消费者处于WAITING,系统假死。

上面的分析可以看出,假死出现的原因是因为notify的是同类,所以非单生产者/单消费者的场景,可以采取两种方法解决这个问题:

1、synchronized用notifyAll()唤醒所有线程、ReentrantLock用signalAll()唤醒所有线程

2、用ReentrantLock定义两个Condition,一个表示生产者的Condition,一个表示消费者的Condition,唤醒的时候调用相应的Condition的signal()方法就可以了

这里对比 和 wait/notify ,await()/signal() 可以利用多个Condition 进行消费/生产实现效果,不用通知所有线程,这里显得更加效率,方便


要实现生产消费模型,java提供了队列机制更加方便的实现,参考 java 多线程阻塞队列 与 阻塞方法与和非阻塞方法

最新文章

  1. Date类
  2. 命令查询网站是否开启CDN加速
  3. 419. Battleships in a Board
  4. webView--总结
  5. JAVA中封装JSONUtils工具类及使用
  6. Codevs 2833 奇怪的梦境
  7. 误用ArrayListMultimap引发的问题
  8. 【转】 为SeekBar滑块设置固定值以及自定义Seekbar,progressbar样式--不错
  9. tesseract ocr文字识别
  10. [android] 手机卫士黑名单功能(ListView结合SQLite增删改)
  11. Twitter数据抓取的方法(三)
  12. UVa12563- Jin Ge Jin Qu hao
  13. Python系列:四、Python函数--技术流ken
  14. Linux内容
  15. MyBatis中的if写法
  16. Module build failed: Error: Cannot find module &#39;url-loader&#39; 的坑
  17. sorted()排序详解
  18. JS中方法判断存在
  19. php对接网易云信视频直播
  20. 安装oracle 11gr2 rac on solaris

热门文章

  1. 实现仿UC浏览器首页下拉动画
  2. 树莓派UFW防火墙简单设置
  3. CentOS 开启防火墙 firewall ,mysql 远程访问
  4. jquery datetimepicker 配置参数
  5. 一个MVC4 下的验证码用法
  6. Easyui combobox onChange事件
  7. nginx last 和break redirect 和 permanent
  8. FreeSWITCH在会议室中持续播放音频文件
  9. iOS 9 学习系列: Xcode Code Coverage
  10. flowable 的ProcessEngine配置