简述
生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一存储空间,生产者向缓存区里生产数据,而消费者从缓存区里取出数据
什么是生产者?
生产者指的是负责生产数据的模块。
什么是消费者?
消费者指的是负责消费数据的模块。
什么是缓冲区?
消费者不能直接使用生产者的数据,它们之间有个缓冲区。生产者将生产好的数据放入缓冲区,消费者从缓冲区取要处理的数据。
注:上面的的模块指的可能是:方法、对象、线程等。
缓冲区是实现并发协助的核心,设置缓冲区有几个好处:
1.实现线程的并发协作
有了缓冲区以后,生产者线程只需往缓冲区里面放置数据,不需要管消费者消费的情况。同样,消费者只需要从缓冲区取数据处理,不需要管生产者生产的情况。 这样就从逻辑上实现了生 产者线程和消费者线程的分离。
2.解耦了生产者和消费者
生产者不需要和消费者直接打交道。
3.解决忙闲不均,提高效率
生产者生产数据慢时,缓冲区仍有数据,不影响消费者消费。消费者处理数据慢时,生产者仍然可以继续往缓冲区里面放置数据 。
代码实现
1 .使用 lock锁机制实现, 通过创建Lock对象,采用lock()加锁,unlock()解锁。
class Resource {
private int num;
private Lock reentrantLock = new ReentrantLock();
private Condition condition = reentrantLock.newCondition();
//生产
public void add(){
//消费了再生产 不等于0时循坏等待
reentrantLock.lock();
try {
while (num!=0){
condition.await();
}
System.out.println(Thread.currentThread().getName()+"生产 +1");
num++;
condition.signalAll();//通知消费 唤醒所有wait状态的线程
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
public void minus(){
//生产了再消费 等于0时循坏等待
reentrantLock.lock();
try {
while (num==0){
condition.await();
}
System.out.println(Thread.currentThread().getName()+"消费 -1");
num--;
condition.signalAll();//通知消费 唤醒所有wait状态的线程
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
}
public class ProductionAndConsumer {
public static void main(String[] args) {
Resource resource = new Resource();
for (int i = 1; i <6 ; i++) {
new Thread(()->{
resource.add();
},"生产线程").start();
}
for (int i = 1; i <6 ; i++) {
new Thread(()->{
resource.minus();
},"消费线程").start();
}
}
}
运行输出:
2.使用阻塞队列实现
public class ProductionAnDConsumerBlockingQueue {
public static void main(String[] args) throws InterruptedException {
Resource resource = new Resource(new LinkedBlockingQueue(10));
new Thread(()->{
try {
resource.prod();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"prod").start();
new Thread(()->{
try {
resource.consumer();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"consumer").start();
TimeUnit.SECONDS.sleep(5);
System.out.println("按下关闭开关,停止生产、消费");
resource.stop();
}
static class Resource {
///生产开关
private volatile boolean flag = true;
private AtomicInteger num = new AtomicInteger();
//阻塞队列
private BlockingQueue queue = null;
public Resource(BlockingQueue queue) {
this.queue = queue;
}
/*
*生产队列
**/
public void prod() throws InterruptedException {
String temp ;
boolean offer;
while (flag){
temp = num.incrementAndGet()+"";
//生产模式开启
offer = queue.offer(temp, 2l, TimeUnit.SECONDS);
// if(offer){
System.out.println(Thread.currentThread().getName()+"生产成功,插入队列成功:"+temp);
// }else {
// System.out.println("生产失败,插入队列失败:"+temp);
// }
//模拟业务操作耗时时间
TimeUnit.SECONDS.sleep(1);
}
System.out.println("生产总开关关闭了 状态:"+flag);
}
/*
* 消费队列
**/
public void consumer() throws InterruptedException{
String poll;
while (flag){
poll = (String) queue.poll(2l, TimeUnit.SECONDS);
if(poll==null||poll.equalsIgnoreCase("")){
System.out.println("等待超时,停止消费");
this.flag=false;
return;
}
System.out.println(Thread.currentThread().getName()+"消费成功"+poll);
}
System.out.println("消费队列检查到总开关关闭了 状态:"+flag);
}
public void stop(){
this.flag=false;
}
}
}
运行输出