Java多线程设计模式详解学习笔记七——Producer-Consumer

public class Main { public static void main(String[] args) { Table table=new Table(3); new MakerThread("MakerThread-1", table, 12345).start(); new MakerThread("MakerThread-2", table, 54345).start(); new MakerThread("MakerThread-3", table, 62345).start(); new EaterThread("EaterThread-1", table, 23455).start(); new EaterThread("EaterThread-2", table, 43455).start(); new EaterThread("EaterThread-3", table, 53455).start(); } }public class EaterThread extends Thread{ private final Table table; private final Random random; public EaterThread(String name,Table table,long seed) { super(name); this.table=table; random=new Random(seed); } @Override public void run() { try { while (true) { String cake=table.take(); Thread.sleep(random.nextInt(1000)); } } catch (Exception e) { // TODO: handle exception } } }public class MakerThread extends Thread{ private final Table table; private final Random random; private static int id=0; public MakerThread(String name,Table table,long seed) { super(name); this.table=table; random=new Random(seed); } @Override public void run() { try { while (true) { Thread.sleep(random.nextInt(1000)); String cake="[ Cake No: "+nextId()+" by "+getName()+"]"; table.put(cake); } } catch (Exception e) { // TODO: handle exception } } private static synchronized int nextId() { return ++id; } } public class Table { private final String[] buffer; private int count; private int tail; private int head; public Table(int count) { buffer=new String[count]; count=0; tail=0; head=0; } public synchronized String take() throws InterruptedException { while (count<=0) { wait(); } String cake=buffer[head]; head=(head+1)%buffer.length; count--; notifyAll(); System.out.println(Thread.currentThread().getName()+" takes "+cake); return cake; } public synchronized void put(String cake) throws InterruptedException { System.out.println(Thread.currentThread().getName()+" put "+cake); while (count>=buffer.length) { wait(); } buffer[tail]=cake; tail=(tail+1)%buffer.length; count++; notifyAll(); } }

运行就过如下:

MakerThread-1 put [ Cake No: 1 by MakerThread-1]
EaterThread-2 takes [ Cake No: 1 by MakerThread-1]
MakerThread-1 put [ Cake No: 2 by MakerThread-1]
EaterThread-1 takes [ Cake No: 2 by MakerThread-1]
MakerThread-1 put [ Cake No: 3 by MakerThread-1]
EaterThread-3 takes [ Cake No: 3 by MakerThread-1]
MakerThread-3 put [ Cake No: 4 by MakerThread-3]
EaterThread-2 takes [ Cake No: 4 by MakerThread-3]
MakerThread-2 put [ Cake No: 5 by MakerThread-2]
EaterThread-2 takes [ Cake No: 5 by MakerThread-2]
MakerThread-3 put [ Cake No: 6 by MakerThread-3]
EaterThread-1 takes [ Cake No: 6 by MakerThread-3]
MakerThread-1 put [ Cake No: 7 by MakerThread-1]
EaterThread-3 takes [ Cake No: 7 by MakerThread-1]
MakerThread-1 put [ Cake No: 8 by MakerThread-1]
MakerThread-1 put [ Cake No: 9 by MakerThread-1]
EaterThread-2 takes [ Cake No: 8 by MakerThread-1]
EaterThread-1 takes [ Cake No: 9 by MakerThread-1]
MakerThread-2 put [ Cake No: 10 by MakerThread-2]
MakerThread-2 put [ Cake No: 11 by MakerThread-2]

Producer-Consumer Pattern 的所有参与者

程序的类图如下



Producer是“生产者”的意思,是指产生数据的线程,而Consumer是“消费者”的意思,意指使用数据的线程。

生产者必须将数据安全的交给消费者。虽然这是这样的问题,但当生产者和消费者在不同线程上运行时,两者的处理速度差将是最大的问题。当消费者要取数据时生产者还没建立数据,或者是生产者建立出数据时消费者的状态还没办法接受数据等等。
Producer-Consumer Pattern是在生产者与消费者之间加入一个“桥梁参与者”,以这个桥梁参与者缓冲线程之间的处理速度差。Channel参与者介于Producer参与者、Consumer参与者之间,担任传递Data参与者中继站,通道的角色。

保护安全性的Channel参与者
范例程序中,Table类的put和take方法都是用了Guarded Suspension Pattern,但MakerThread类与EaterThread类都不想依于Table类的详细实现,也就是说MakerThread不比理会其他线程,只要调用put方法就好,而EaterThread也是只要会调用take方法就可以。使用synchronized、wait、notifyAll这些考略到多线程操作的程序代码,全部隐藏在Channel参与者Table类里面。

中间者存在的隐含的意义
因为Channel参与者的存在,Producer参与者和Consumer参与者这条多线程,才能保持协力、合作。而在在Single Thread Execution Pattern中曾经说过“Synchronized是在保护什么”?当时我们说过,思索线程的共享互斥问题时,把观察切入点放在“保护着什么”上面,会比较容易找到问题的结症。
上面的思维若整理成口诀,可以得到两句话:
一。线程的合作要想“放在中间的东西”
二。线程的互斥要想“应该保护的东西”

InterruptedException异常
Java的标准类链接库中,后面接着throws InterrupedException的方法的代表选手,有下面这3位:
Java.lang.Object类的wait方法
Java.lang.Thread类的sleep方法
Java.lang.Thread类的join方法
这个通常告诉我们下面两件事:
一、这是“需要花点时间”的方法
二、这是“可以取消”的方法
用一句话来说,后面接着throws InterruptedException的方法,是可能会花一点时间,但是可以取消的方法。
一、需要花点时间的方法
执行wait方法的线程,会进入等待区等待被notify/notifyAll,在等待期间,线程不会活动。故需要花费等待被notify/notifyAll的时间。
执行sleep方法的线程,会暂停执行参数内所设置的时间,这也是花费时间的方法。
执行join方法的线程,会等待到指定的线程结束为止。也就是会花费直到指定线程结束之前的这段时间
像上面三个方法,分别要等待“被notify/notifyAll、设置的时间、指定的线程结束”,的确都是“需要花点时间”的方法
二、可以取消的方法
因为需要画时间的操作,会降低程序的响应性,所以我们会希望像下面着阳光可以在中途放弃(取消)执行这个方法
1、取消wait方法等待notify/notifyAll的操作
2、取消sleep方法等待设置长度时间的限制
3、取消join方法等待其他线程结束的操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值