生产者消费者模型
生产者消费者模型是什么
生产者和消费者彼此之间不直接通讯,而是通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取。阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
这个阻塞队列就是用来给生产者和消费者解耦的。纵观大多数设计模式,都会找一个第三者出来进行解耦,如工厂模式的第三者是工厂类,模板模式的第三者是模板类。生产者消费者模型属于架构设计,类似于我们所说的设计模式,用于解决生产者和消费者速率不匹配的问题这一类问题,架构设计上使得消费者生产者解耦。
wait()方法
① 使当前线程进行等待,将当前线程置于“预执行队列”中,要想继续执行,除非被 notify() 或被中断
② 要在同步代码块或同步方法中使用,否则会报错!!!
③ wait() 执行完成后,当前线程释放锁,进行下一轮竞争
public class TestWait { public static void main(String[] args) { Object object = new Object(); synchronized (object) { System.out.println("开始执行"); try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("结束执行"); } } }
从上述代码我们发现执行object.wait()之后程序就在一直等待下去,那么程序肯定不能一直这么等待下去了。这个时候就需要使用到了另外一个唤醒线程的方法notify()。
notify()方法
① 通知那些可能等待该对象的对象锁的线程,对其发出通知 notify,使它们重新获得该对象的对象锁
② 如果有多个线程在等待,则由线程规划器随机挑选一个呈 wait 状态的线程,该方法会造成阻塞
③ 要在同步代码块或同步方法中使用,否则会报错!!!
④ notify() 方法不会马上释放对象锁,要等 notify() 方法的线程将程序执行完,即退出同步代码块之后才会释放对象锁
public class TestNotify { public static void main(String[] args) { final Object monitor = new Object(); Thread threadWait = new MyWaitThread(monitor); threadWait.setName("Thread-wait"); Thread threadNotify = new MyNotifyThread(monitor); threadNotify.setName("Thread-notify"); threadWait.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } threadNotify.start(); } } class MyWaitThread extends Thread { private final Object monitor; public MyWaitThread(Object monitor) { this.monitor = monitor; } @Override public void run() { synchronized (this.monitor) { System.out.println(Thread.currentThread().getName() + "开始执行"); try { this.monitor.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "结束执行"); } } } class MyNotifyThread extends Thread { private final Object monitor; public MyNotifyThread(Object monitor) { this.monitor = monitor; } @Override public void run() { synchronized (this.monitor) { System.out.println(Thread.currentThread().getName() + "开始执行"); //通知一个线程 this.monitor.notify(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "结束执行"); } } }
第一个线程里面有个死循环并且使用了 wait 方法进入等待状态,如果这个线程不被唤醒的话将会一直等待下去,这个时候第二个线程执行的是 notify 方法,将线程一唤醒继续执行。
notifyAll()方法
以上所说的 notify 方法只是唤醒某一个等待线程,那么如果有多个线程都在等待中怎么办呢,这个时候就可以使用 notifyAll 方法可以一次唤醒所有的等待线程。
public class TestNotify { public static void main(String[] args) { final Object monitor = new Object(); Thread threadWait = new MyWaitThread(monitor); threadWait.setName("Thread-wait"); Thread threadWait2 = new MyWaitThread(monitor); threadWait2.setName("Thread-wait2"); Thread threadNotify = new MyNotifyThread(monitor); threadNotify.setName("Thread-notify"); threadWait.start(); threadWait2.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } threadNotify.start(); } } class MyWaitThread extends Thread { private final Object monitor; public MyWaitThread(Object monitor) { this.monitor = monitor; } @Override public void run() { synchronized (this.monitor) { System.out.println(Thread.currentThread().getName() + "开始执行"); try { this.monitor.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "结束执行"); } } } class MyNotifyThread extends Thread { private final Object monitor; public MyNotifyThread(Object monitor) { this.monitor = monitor; } @Override public void run() { synchronized (this.monitor) { System.out.println(Thread.currentThread().getName() + "开始执行"); //通知多个线程 this.monitor.notifyAll(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "结束执行"); } } }
注意:不能过早地唤醒线程。如果在还没有线程在等待时,过早的唤醒线程,这个时候就会出现先唤醒再等待的效果了。这样就没有必要在去执行 wait 方法了。
生产者消费者模型
生产者与消费者开头已经介绍过了,生产者与消费者一般需要第三者来解耦的,所以现在就模拟一个简单的生产者与消费者,由生产者线程生产出一个商品之后将由消费者线程开始消费!
商品类
/** * Goods:生产者生产的产品类 */ public class Goods { private final String id; private final String name; public Goods(String id, String name) { this.id = id; this.name = name; } public String getId() { return id; } public String getName() { return name; } @Override public String toString() { return "Goods{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}'; } }
生产者
/** * 生产者: * 1.生产商品 * 2.将生产好的商品添加到容器中 * 3.若容器已满,等待消费者消费 */ import java.util.Queue; import java.util.concurrent.atomic.AtomicInteger; public class Producer implements Runnable { private final Queue<Goods> queue; private final Integer maxCapacity = 10; //原子变量 private final AtomicInteger id = new AtomicInteger(0); public Producer(Queue<Goods> queue) { this.queue = queue; } @Override public void run() { while(true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (this.queue) { if(queue.size() == maxCapacity) { System.out.println(Thread.currentThread().getName() + "容器满了,等待消费"); try { this.queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { //获取id: //UUID.randomUUID().toString()/AtomicInteger //getAndIncrement():获取之后再增加 Goods good = new Goods(String.valueOf(id.getAndIncrement()), "A商品"); System.out.println(Thread.currentThread().getName() + "生产商品" + good); this.queue.add(good); } } } } }
消费者
/** * 消费者: * 1.消费商品 * 2.从容器中取出商品 * 3.若容器为空,通知生产者生产 */ import java.util.Queue; public class Customer implements Runnable { private final Queue<Goods> queue; public Customer(Queue<Goods> queue) { this.queue = queue; } @Override public void run() { while(true) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (this.queue) { if(this.queue.isEmpty()) { System.out.println(Thread.currentThread().getName() + "容器已空,通知生产"); this.queue.notifyAll(); } else { Goods good = this.queue.poll(); if(good != null) { System.out.println(Thread.currentThread().getName() + "消费商品" + good); } } } } } }
@Test:
import java.util.LinkedList; import java.util.Queue; public class TestProducerCustomer { public static void main(String[] args) { final Queue<Goods> queue = new LinkedList<>(); final Runnable producer = new Producer(queue); final Runnable customer = new Customer(queue); //生产者线程 for(int i = 0; i < 5; i++) { new Thread(producer, "Thread-producer-" + i).start(); } //消费者线程 for(int i = 0; i < 8; i++) { new Thread(customer, "Thread-customer-" + i).start(); } } }