【孔乙已】生产者消费者有四样写法,系统工程师面试问题

本文详细介绍了Java中四种实现生产者消费者模式的方法,强调了在使用wait/notify进行线程通信时需要注意的两点:始终在循环中判断条件,并使用notifyAll()代替notify()。并提供了具体的代码实现,包括使用synchronized关键字、ReentrantLock以及BlockingQueue的实现方式。
摘要由CSDN通过智能技术生成

4.Kotlin Channel方式实现

1.wait/notify方式实现

Java 中,可以通过配合调用 Object 对象的 wait() 方法和 notify()方法或 notifyAll() 方法来实现线程间

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

的通信。
在线程中调用 wait() 方法,将阻塞当前线程,同时释放对象锁,直至等到其他线程调用了 notify() 方法或 notifyAll() 方法进行通知之后,当前线程才能从wait()方法中返回,继续执行下面的操作

其中要注意两点

1.永远使用while循环对wait条件进行判断,而不是if语句中进行wait条件的判断

现象
当有多个消费者时,使用if会抛出异常

原因分析
当使用if做条件判断时,当线程被唤醒后就继续执行消费者方法,但此时如果元素已经被另一个消费者消费了,那么就会抛出异常

解决方案
永远使用while循环对wait条件进行判断,而不是if语句中进行wait条件的判断

2.使用NotifyAll而不是使用notify

现象
如果是多消费者和多生产者情况,如果使用notify方法可能会出现“假死”的情况,即唤醒的是同类线程。

原因分析
假设当前多个生产者线程会调用wait方法阻塞等待,当其中的生产者线程获取到对象锁之后使用notify通知处于WAITTING状态的线程,如果唤醒的仍然是生产者线程,就会造成所有的消费者线程都处于等待状态。

解决方案
使用NotifyAll而不是使用notify

代码实现如下:

public class ProductorConsumerDemo1 {

public static void main(String[] args) {

LinkedList linkedList = new LinkedList();
ExecutorService service = Executors.newFixedThreadPool(15);
for (int i = 0; i < 5; i++) {
service.submit(new Productor(linkedList, 8));
}

for (int i = 0; i < 10; i++) {
service.submit(new Consumer(linkedList));
}

}

static class Productor implements Runnable {

private List list;
private int maxLength;

public Productor(List list, int maxLength) {
this.list = list;
this.maxLength = maxLength;
}

@Override
public void run() {
while (true) {
synchronized (list) {
try {
while (list.size() == maxLength) {
System.out.println(“生产者” + Thread.currentThread().getName() + " list以达到最大容量,进行wait");
list.wait();
System.out.println(“生产者” + Thread.currentThread().getName() + " 退出wait");
}
Random random = new Random();
int i = random.nextInt();
System.out.println(“生产者” + Thread.currentThread().getName() + " 生产数据" + i);
list.add(i);
list.notifyAll();
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}
}
}

static class Consumer implements Runnable {

private List list;

public Consumer(List list) {
this.list = list;
}

@Override
public void run() {
while (true) {
synchronized (list) {
try {
while (list.isEmpty()) {
System.out.println(“消费者” + Thread.currentThread().getName() + " list为空,进行wait");
list.wait();
System.out.println(“消费者” + Thread.currentThread().getName() + " 退出wait");
}
Integer element = list.remove(0);
System.out.println(“消费者” + Thread.currentThread().getName() + " 消费数据:" + element);
list.notifyAll();
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}

}

2.Lock方式实现

Lock中的Condition可以实现上面Objectwait,notify一样的效果
await对应wait,signal对应notify,signalAll对应notifyAll
下面直接来看看实现,代码与使用wait,notify基本上是一样的,只是同步方式不同

public class ProductorConsumerDemo2 {

private static ReentrantLock lock = new ReentrantLock();
private static Condition full = lock.newCondition();
private static Condition empty = lock.newCondition();

public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
ExecutorService service = Executors.newFixedThreadPool(15);
for (int i = 0; i < 5; i++) {
service.submit(new Productor(linkedList, 8, lock));
}
for (int i = 0; i < 10; i++) {
service.submit(new Consumer(linkedList, lock));
}

}

static class Productor implements Runnable {

private List list;
private int maxLength;
private Lock lock;

public Productor(List list, int maxLength, Lock lock) {
this.list = list;
this.maxLength = maxLength;
this.lock = lock;
}

@Override
public void run() {
while (true) {
lock.lock();
try {
while (list.size() == maxLength) {
System.out.println(“生产者” + Thread.currentThread().getName() + " list以达到最大容量,进行wait");
full.await();
System.out.println(“生产者” + Thread.currentThread().getName() + " 退出wait");
}
Random random = new Random();
int i = random.nextInt();
System.out.println(“生产者” + Thread.currentThread().getName() + " 生产数据" + i);
list.add(i);
empty.signalAll();
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}

static class Consumer implements Runnable {

private List list;
private Lock lock;

public Consumer(List list, Lock lock) {
this.list = list;
this.lock = lock;
}

@Override
public void run() {
while (true) {
lock.lock();
try {
while (list.isEmpty()) {
System.out.println(“消费者” + Thread.currentThread().getName() + " list为空,进行wait");
empty.await();
System.out.println(“消费者” + Thread.currentThread().getName() + " 退出wait");
}
Integer element = list.remove(0);
System.out.println(“消费者” + Thread.currentThread().getName() + " 消费数据:" + element);
full.signalAll();
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}

}

3.BlockingQueue方式实现

由于BlockingQueue内部实现就附加了两个阻塞操作。
即当队列已满时,阻塞向队列中插入数据的线程,直至队列中未满;当队列为空时,阻塞从队列中获取数据的线程,直至队列非空时为止.
所以使用BlockingQueue来实现生产者消费者模式非常简单方便,关于BlockingQueue的更多细节可见:并发容器之BlockingQueue详解

下面直接看下生产者消费者实现

public class ProductorConsumerDmoe3 {

private static LinkedBlockingQueue queue = new LinkedBlockingQueue<>();

public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(15);
for (int i = 0; i < 5; i++) {
service.submit(new Productor(queue));
}
for (int i = 0; i < 10; i++) {
service.submit(new Consumer(queue));
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值