生产者-消费者模型是一个多线程并发协作得模型。主要思想是:通过一个阻塞队列让生产者和消费者之间间接通信,生产者生产完数据后不用等待消费者处理,而是交给阻塞队列,消费者也不用跟生产者直接要数据,而是从阻塞队列里取。这个阻塞队列解决了生产者和消费者之间强耦合的问题,平衡了生产者和消费者的处理能力。
模拟生产者和消费者:Main类(5个消费者和1个生产者)
public static void main(String[] args) {
//阻塞队列
MyBlockingQueue2 taskQueue = new MyBlockingQueue2();
//生成任务编号
AtomicInteger taskNumber = new AtomicInteger(1);
//创建固定数量的线程池(一个生产者,五个消费者)
ExecutorService executorService = Executors.newFixedThreadPool(6);
//5个消费者
for(int i=0;i<5;i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
while(true) {
try {
//消费者不断的从阻塞队列中获取(消费)任务
String item = taskQueue.take();
System.out.println("[消费者]"+Thread.currentThread().getName()+"获取任务"+item);
} catch (InterruptedException e) {
System.out.println("[消费者]"+Thread.currentThread().getName()+"中断结束!");
}
}
}
});
}
//生产者
executorService.execute(new Runnable() {
@Override
public void run() {
//10个任务
for(int i=0;i<10;i++) {
String s = "task"+taskNumber.getAndIncrement();
System.out.println("[生产者]生成任务:"+s);
//将生成的任务放入阻塞队列
taskQueue.put(s);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//10个任务提交结束
executorService.shutdownNow();
}
});
}
自定义阻塞队列:MyBlockingQueue类(使用notifyAll+wait的方式)
put()方法用于模拟生产者将数据任务放入阻塞队列中,并调用notifyll()方法唤醒所有的线程任务。
take()方法用于模拟消费者从阻塞队列中获取队头数据任务,在获取锁时,先判断阻塞队列是否为空,如果为空,让线程进入等待并释放锁,被put(()方法的this.notifyAll();唤醒时,会重新获取锁。
public class MyBlockingQueue {
//使用LinkedList 作为队列的存储容器
private Queue<String> queue = new LinkedList<String>();
//添加元素(生产的数据任务)放入队尾
public synchronized void put(String task) {
//将数据任务放入阻塞队列中
queue.offer(task);
//唤醒所有线程(通知所有线程有线程任务进入阻塞队列)
this.notifyAll();
}
//获取队列的对头元素(消费者获取数据任务)
public synchronized String take() throws InterruptedException{
//判断阻塞队列会否为空
while(queue.isEmpty()) {
//获取this锁(如果队列为空)
this.wait();//进入等待,释放this锁
//被put()的notifyAll唤醒时
//this.notifyAll() 让当前线程重新获取锁
}
//返回队头元素
return queue.poll();
}
}
自定义阻塞队列:MyBlockingQueue2类(使用signalAll+await的方式)
使用ReentrantLock进行线程同步,使用ReentranLock可以通过Condition的signalAll()和await()。
signalAll():唤醒所有等待的线程
signal():随机唤醒等待线程中得一个
await():无限等待
await(等待毫秒值):等待某一时间后自动被唤醒
public class MyBlockingQueue2 {
//使用LinkedList 作为队列的存储容器
private Queue<String> queue = new LinkedList<String>();
//ReentrantLock锁
private final ReentrantLock lock = new ReentrantLock();
//Condition接口:定义了signalAll()和await()作用等同于notifyAll()和wait()
private final Condition condition = lock.newCondition();
//添加元素(生产的数据任务)放入队尾
public void put(String task) {
//定义临时变量,引用当前锁
final ReentrantLock lock = this.lock;
//加锁
lock.lock();
try {
queue.offer(task);
//唤醒所有线程(通知所有线程有线程任务进入阻塞队列)
condition.signalAll();//作用等同于 notify()
}finally{
//释放锁
lock.unlock();
}
}
//获取队列的对头元素(消费者获取数据任务)
public synchronized String take() throws InterruptedException{
final ReentrantLock lock = this.lock;
lock.lock();
try {
while(queue.isEmpty()) {
//获取this锁(队列为空)
condition.await();//作用等同于 wait() ,无限等待,通过signalAll()唤醒
//condition.await(1,TimeUnit.SECONDS);//进入等待,1秒钟超时后唤醒,作用等同于wait()
}
//返回队头元素
return queue.poll();
}finally {
lock.unlock();
}
}
}