1.场景
有些时候需要两个或两个以上的线程协同工作,每个线程需要使用其他线程产生数据。
2.详细说明
可以把上面的场景抽象成生产者和消费者模式。从消费者的角度:消费者需要消费生产者生成的产品。
从生产者的角度:生产者需要将生产的产品安全地交给消费者。这虽然看着是一个简单的操作。但如果生产者和消费者在不同的线程中运行,两者之间的处理速度差异会引起问题:消费者消费的速度远快于生产者生产的速度即消费者想要获取数据,而数据还没有产生,或者消费者消费的速度远慢于生产者生产的速度即生产者想要交付数据时,消费者还处在无法接收数据的状态。
3.Producer-Consumer模式
Producer-Consumer模式在生产者和消费者中间加入”桥梁角色”。利用该角色来消除不同线程间的处理速度的差异。
要实现上述的场景,我们设计这样一个场景:
在一家制作蛋糕店里,糕点师制作蛋糕,并将蛋糕放在桌子上。顾客按顺序从桌子上拿蛋糕吃。、
说明:1.桌子上最多只能放3个蛋糕;2.若桌子已经放满3个蛋糕,糕点师必须等到桌子空出位置才能放置蛋糕;3.如果桌子上没有蛋糕,顾客必须等待桌子上有蛋糕才能吃。
因此我们需要设计4个类:糕点师类MakerThread,顾客类EaterThread,桌子类Table,启动线程的主类Main。类图如下:
源码如下:
public class Table {private final String[] buffer;private int tail;//下次put的位置private int head;//下次take的位置private int count;//buffer中蛋糕的个数public Table(int count){this.buffer=new String[count];this.head=0;this.tail=0;this.count=0;}//放置产品(蛋糕)public synchronized void addCake(String cake) throws InterruptedException {StdOut.println(Thread.currentThread().getName()+" puts "+cake);while(count>=buffer.length){wait();}buffer[tail]=cake;tail=(tail+1)%buffer.length;count++;notifyAll();}//拿取蛋糕public synchronized String reduceCake() throws InterruptedException{while(count<=0){wait();}String cake=buffer[head];head=(head+1)%buffer.length;count--;notify();StdOut.println(Thread.currentThread().getName()+" takes "+cake);return cake;}}
在Table类中我们采用数组来存放蛋糕,同时设计两个方法:addCake()和reduceCake()来对桌子上蛋糕数量等状态进行修改。
在addCake()中线程的守护条件是:count>=buffer.length;如果满足该条件则表示桌子已放满蛋糕,糕点师需要等待,直到桌子空出位置,才能放置。
在reduceCake()中线程的守护条件是:count<=0;如果满足该条件则表示桌子已经没有蛋糕了,顾客需要等待,等到桌子上有蛋糕时才能吃。
糕点师类:
public class MakerThread extends Thread {private final Random random;private final Table table;private static int id=0;//蛋糕的流水号public MakerThread(String name,Table table,long seed){super(name);this.table=table;this.random=new Random(seed);}public void run(){try {while(true){Thread.sleep(random.nextInt(1000));String cake="[ Cake No."+nextId()+" by "+getName()+" ]";table.addCake(cake);}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}private static synchronized int nextId(){return id++;}}
顾客吃蛋糕类:
public class EaterThread extends Thread {private final Random random;private final Table table;public EaterThread(String name,Table table,long seed){super(name);this.table=table;this.random=new Random(seed);}public void run(){try {while(true){Thread.sleep(random.nextInt(1000));String cake=table.reduceCake();}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
启动线程类:
public class Main {public static void main(String[] args) {// TODO Auto-generated method stubTable table=new Table(3);//创建一个能放置3个蛋糕的桌子new MakerThread("MakerThread-1", table, 31415).start();new MakerThread("MakerThread-2", table, 31415).start();new MakerThread("MakerThread-3", table, 31415).start();new EaterThread("EaterThread-1", table, 161415).start();new EaterThread("EaterThread-2", table, 71415).start();new EaterThread("EaterThread-3", table, 81415).start();}}
说明:学习《图解java多线程设计模式》的学习笔记整理和自己的理解。
更多技术干货,请关注下面二维码: