java多线程基本概述(六)——简单生产者消费者模式
在线程里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。下面实现一个简单的生产者消费者模式:
1.一个消费者一个生产者循环消费生产
package soarhu; import java.util.ArrayList; import java.util.List; class Service{ private final Object lock; private List<String> list = new ArrayList<>(); public Service(Object lock) { this.lock = lock; } void waiting(){ synchronized (lock){ try { while(list.size()==0){ lock.wait(); } String value = list.remove(0); System.out.println("consume: "+value); lock.notifyAll(); } catch (InterruptedException e) { e.printStackTrace(); } } } void notifying(){ synchronized (lock){ try { while(list.size()!=0){ lock.wait(); } String value=System.currentTimeMillis()+""; list.add(value); System.out.println("produce: "+value); lock.notifyAll(); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Test { public static void main(String[] args) throws InterruptedException { Object o = new Object(); Service service = new Service(o); for (int i = 0; i < 1; i++) { //这里的数量为1 new Thread(){ @Override public void run() { while (true) { service.notifying(); } } }.start(); } Thread.sleep(1000); for (int i = 0; i < 1; i++) { //此时数量改为1 new Thread(){ @Override public void run() { while (true) { service.waiting(); } } }.start(); } } }
输出结果
produce: 1492495274866 consume: 1492495274866 produce: 1492495274866 consume: 1492495274866 produce: 1492495274866 consume: 1492495274866 produce: 1492495274866 consume: 1492495274866 produce: 1492495274866 consume: 1492495274866 produce: 1492495274866 consume: 1492495274866 produce: 1492495274866 consume: 1492495274866 produce: 1492495274866 consume: 1492495274866 produce: 1492495274866 consume: 1492495274866 produce: 1492495274866 consume: 1492495274866 produce: 1492495274866 consume: 1492495274866 .............. .......... .........
这里可能有个踩坑的地方需要注意,代码改为如下后:
1 void notifying(){ 2 synchronized (lock){ 3 try { 4 int size = list.size(); 5 System.out.println("before produce size: "+size); 6 while(size!=0){ 7 System.out.println(Thread.currentThread().getName()+" PRODUCE WAITING..."+size); 8 lock.wait(); 9 } 10 String value=System.currentTimeMillis()+""; 11 list.add(value); 12 System.out.println(Thread.currentThread().getName()+" produce: "+value); 13 lock.notifyAll(); 14 System.out.println("after produce size: "+list.size()); 15 } catch (InterruptedException e) { 16 e.printStackTrace(); 17 } 18 } 19 }
输出结果:死锁
before produce size: 0 Thread-0 produce: 1492507566267 after produce size: 1 before produce size: 1 Thread-0 PRODUCE WAITING...1 Thread-1 consume: 1492507566267 Thread-1 CONSUME WAITING... Thread-0 PRODUCE WAITING...1
之所以会产生死锁是因为:list.size()的的判断条件没有再while循环中,那么再次求size的大小时,size的值并不时最新的,因为生产者线程从wait()苏醒后走的是while条件。那么将list.size()放置到while()条件中即可正常。
其中管道流是一种特殊的流,用于再不同的线程间直接通过管道传送数据。一个线程发送数据到管道,另一个线程从输入管道读取数据。
例如:
package soarhu; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; class Service{ void readMethod(PipedInputStream inputStream){ try { System.out.println("read: "); byte[] bytes = new byte[20]; int readLength = inputStream.read(bytes); while (readLength!=-1){ readLength = inputStream.read(bytes); } System.out.println("read finish......"); inputStream.close(); }catch (IOException e){ e.printStackTrace(); } } void writeMethod(PipedOutputStream outStream){ try { System.out.println("write: "); for (int i = 0; i < 30; i++) { String outData = ""+(i+1); outStream.write(outData.getBytes()); System.out.println("write "+outData+" -> "+Thread.currentThread().getName()); } System.out.println("write finish......"); outStream.close(); }catch (IOException e){ e.printStackTrace(); } } } public class Test { public static void main(String[] args) throws Exception { Service o = new Service(); PipedInputStream inputStream = new PipedInputStream(); PipedOutputStream outputStream = new PipedOutputStream(); outputStream.connect(inputStream); for (int i = 0; i < 1; i++) { new Thread(){ @Override public void run() { o.readMethod(inputStream); } }.start(); } Thread.sleep(5000); for (int i = 0; i < 1; i++) { new Thread(){ @Override public void run() { o.writeMethod(outputStream); } }.start(); } } }
read: write: write 1 -> Thread-1 write 2 -> Thread-1 write 3 -> Thread-1 write 4 -> Thread-1 write 5 -> Thread-1 write 6 -> Thread-1 write 7 -> Thread-1 write 8 -> Thread-1 write 9 -> Thread-1 write 10 -> Thread-1 write 11 -> Thread-1 write 12 -> Thread-1 write 13 -> Thread-1 write 14 -> Thread-1 write 15 -> Thread-1 write 16 -> Thread-1 write 17 -> Thread-1 write 18 -> Thread-1 write 19 -> Thread-1 write 20 -> Thread-1 write 21 -> Thread-1 write 22 -> Thread-1 write 23 -> Thread-1 write 24 -> Thread-1 write 25 -> Thread-1 write 26 -> Thread-1 write 27 -> Thread-1 write 28 -> Thread-1 write 29 -> Thread-1 write 30 -> Thread-1 write finish...... read finish...... Process finished with exit code 0
使用队列:
package tij; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.atomic.AtomicInteger; /** * Created by huaox on 2017/4/20. * */ class Apple{ private static AtomicInteger task = new AtomicInteger(1) ; private final int id = task.getAndIncrement(); @Override public String toString() { return "Apple{" +"id=" + id +'}'; } } class Producer implements Runnable { private static AtomicInteger count = new AtomicInteger(0); private final BlockingQueue<Apple> queue; Producer(BlockingQueue<Apple> queue) { this.queue = queue; } public void run() { try { while(!Thread.currentThread().isInterrupted()) { //产生一个Apple需要1秒钟 Thread.sleep(1000); queue.put(produce()); count.incrementAndGet(); if (count.get()==10){ System.out.println("produce apple is all "+count); System.exit(0); } } } catch (InterruptedException ex) { System.out.println("interrupted in produce"); } } private Apple produce() { Apple apple= new Apple(); System.out.println("produce apple: "+apple+" "+System.currentTimeMillis()); return apple; } } class Consumer implements Runnable { private final BlockingQueue<Apple> queue; Consumer(BlockingQueue<Apple> q) { queue = q; } public void run() { try { while(!Thread.currentThread().isInterrupted()) { Thread.sleep(2000);//消费一个产品需要2秒钟 consume(queue.take()); } } catch (InterruptedException ex) { System.out.println("interrupted in produce");} } private void consume(Apple x) { System.out.println("consume apple: "+x+" "+System.currentTimeMillis()); } } class Setup { private static final int P_SIZE=2;//生产者个数 private static final int C_SIZE=6;//消费者个数 public static void main(String[] args) { BlockingQueue<Apple> queue = new LinkedBlockingDeque<>(); System.out.println("job starting"); /* for (int i = 0; i < C_SIZE; i++) { new Thread(new Consumer(queue)).start(); } for (int i = 0; i < P_SIZE; i++) { new Thread(new Producer(queue)).start(); }*/ ExecutorService service = Executors.newCachedThreadPool(); for (int i = 0; i < C_SIZE; i++) { service.execute(new Consumer(queue)); } for (int i = 0; i < P_SIZE; i++) { service.execute(new Producer(queue)); } } }
结果:
job starting produce apple: Apple{id=1} 1492694951683 produce apple: Apple{id=2} 1492694951683 produce apple: Apple{id=4} 1492694952684 produce apple: Apple{id=3} 1492694952684 consume apple: Apple{id=1} 1492694952684 consume apple: Apple{id=4} 1492694952684 consume apple: Apple{id=3} 1492694952684 consume apple: Apple{id=2} 1492694952684 produce apple: Apple{id=6} 1492694953684 produce apple: Apple{id=5} 1492694953684 consume apple: Apple{id=6} 1492694953684 consume apple: Apple{id=5} 1492694953684 produce apple: Apple{id=7} 1492694954686 produce apple: Apple{id=8} 1492694954686 consume apple: Apple{id=7} 1492694954686 consume apple: Apple{id=8} 1492694954686 produce apple: Apple{id=9} 1492694955688 produce apple: Apple{id=10} 1492694955688 consume apple: Apple{id=9} 1492694955688 consume apple: Apple{id=10} 1492694955688 produce apple is all 10 Process finished with exit code 0
package tij; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * Created by huaox on 2017/4/20. * */ class Toast{ public enum Status{DRY,BUTTERED,JAMMED} private Status status = Status.DRY; private final int id; Toast(int id){this.id = id;} void butter(){status=Status.BUTTERED;} void jam(){status=Status.JAMMED;} Status getStatus(){return status;} int getId(){return id;} @Override public String toString() { return "Toast[" +"status=" + status +", id=" + id +']'; } } class ToastQueue<T> extends LinkedBlockingQueue<T>{} class Toaster implements Runnable{//生产吐司 private ToastQueue<Toast> toastQueue; private AtomicInteger count = new AtomicInteger(0); private Random random = new Random(47); Toaster(ToastQueue<Toast> toastQueue) { this.toastQueue = toastQueue; } @Override public void run() { try { while (!Thread.currentThread().isInterrupted()){ TimeUnit.MILLISECONDS.sleep(100+random.nextInt(500)); Toast toast = new Toast(count.getAndIncrement()); System.out.println(toast); toastQueue.put(toast); } } catch (InterruptedException e) { System.out.println("toaster interrupted! "); } System.out.println("toaster off"); } } //给吐司抹黄油 class Butter implements Runnable{ private ToastQueue<Toast> dryQueue,butterQueue; Butter(ToastQueue<Toast> dryQueue, ToastQueue<Toast> butterQueue) { this.dryQueue = dryQueue; this.butterQueue = butterQueue; } @Override public void run() { try { while (!Thread.currentThread().isInterrupted()){ Toast take = dryQueue.take();//阻塞直到上一个步骤做好了 take.butter(); System.out.println(take); butterQueue.put(take); } } catch (InterruptedException e) { System.out.println("butter interrupted! "); } System.out.println("butter off"); } } //给吐司涂酱 class Jam implements Runnable{ private ToastQueue<Toast> butterQueue,finishQueue; Jam(ToastQueue<Toast> butterQueue, ToastQueue<Toast> finishQueue) { this.finishQueue = finishQueue; this.butterQueue = butterQueue; } @Override public void run() { try { while (!Thread.currentThread().isInterrupted()){ Toast take = butterQueue.take();//阻塞直到上一个步骤做好了 take.jam(); System.out.println(take); finishQueue.put(take); } } catch (InterruptedException e) { System.out.println("jam interrupted! "); } System.out.println("jam off"); } } class Easter implements Runnable{ private ToastQueue<Toast> finishQueue; private AtomicInteger count = new AtomicInteger(0); Easter(ToastQueue<Toast> finishQueue) { this.finishQueue = finishQueue; } @Override public void run() { try { while (!Thread.currentThread().isInterrupted()){ Toast toast = finishQueue.take(); //验证吐司是不是按顺序来的,并且是不是涂酱了 if(toast.getId()!=count.getAndIncrement() || toast.getStatus()!=Toast.Status.JAMMED){ System.out.println("ERROR!!"); System.exit(1); }else{ System.out.println("CHOMP "+toast); } } } catch (InterruptedException e) { System.out.println("Eater interrupted"); } System.out.println("eater off"); } } public class ToastOMatic { public static void main(String[] args) throws InterruptedException { ToastQueue<Toast> dryQueue = new ToastQueue<>(), butterQueue = new ToastQueue<>(), finishQueue = new ToastQueue<>(); ExecutorService service = Executors.newCachedThreadPool(); service.execute(new Toaster(dryQueue)); service.execute(new Butter(dryQueue,butterQueue)); service.execute(new Jam(butterQueue,finishQueue)); service.execute(new Easter(finishQueue)); TimeUnit.SECONDS.sleep(7); service.shutdownNow(); } }
Connected to the target VM, address: '127.0.0.1:2223', transport: 'socket' Toast[status=DRY, id=0] Toast[status=BUTTERED, id=0] Toast[status=JAMMED, id=0] CHOMP Toast[status=JAMMED, id=0] Toast[status=DRY, id=1] Toast[status=BUTTERED, id=1] Toast[status=JAMMED, id=1] CHOMP Toast[status=JAMMED, id=1] Toast[status=DRY, id=2] Toast[status=BUTTERED, id=2] Toast[status=JAMMED, id=2] CHOMP Toast[status=JAMMED, id=2] Toast[status=DRY, id=3] Toast[status=BUTTERED, id=3] Toast[status=JAMMED, id=3] CHOMP Toast[status=JAMMED, id=3] Toast[status=DRY, id=4] Toast[status=BUTTERED, id=4] Toast[status=JAMMED, id=4] CHOMP Toast[status=JAMMED, id=4] Toast[status=DRY, id=5] Toast[status=BUTTERED, id=5] Toast[status=JAMMED, id=5] CHOMP Toast[status=JAMMED, id=5] Toast[status=DRY, id=6] Toast[status=BUTTERED, id=6] Toast[status=JAMMED, id=6] CHOMP Toast[status=JAMMED, id=6] Toast[status=DRY, id=7] Toast[status=BUTTERED, id=7] Toast[status=JAMMED, id=7] CHOMP Toast[status=JAMMED, id=7] Toast[status=DRY, id=8] Toast[status=BUTTERED, id=8] Toast[status=JAMMED, id=8] CHOMP Toast[status=JAMMED, id=8] Toast[status=DRY, id=9] Toast[status=BUTTERED, id=9] Toast[status=JAMMED, id=9] CHOMP Toast[status=JAMMED, id=9] Toast[status=DRY, id=10] Toast[status=BUTTERED, id=10] Toast[status=JAMMED, id=10] CHOMP Toast[status=JAMMED, id=10] Disconnected from the target VM, address: '127.0.0.1:2223', transport: 'socket' Toast[status=DRY, id=11] Toast[status=BUTTERED, id=11] Toast[status=JAMMED, id=11] CHOMP Toast[status=JAMMED, id=11]