线程系列目录
- Thread线程从零认识到深层理解——初识
- Thread线程从零认识到深层理解——六大状态
- Thread线程从零认识到深层理解——wait()与notify()
- Thread线程从零认识到深层理解——生产者/消费者模型
- Thread线程从零认识到深层理解——线程安全
- 线程池从零认识到深层理解——初识
- 线程池从零认识到深层理解——进阶
前言
Java中多线程的等待唤醒机制是一种经典的体现"生产者和消费者模型。例如:如工厂生产某种商品,当库存不足时就要补充生产,当仓库货满时,就要停止生产该类商品。
生产者/消费者模型需要满足如下几点:
- 生产者生产数据到缓冲区中,消费者从缓冲区中取数据。
- 如果缓冲区已经满了,则生产者线程阻塞,生产者通知消费者消费
- 如果缓冲区为空,那么消费者线程阻塞,消费者通知生产者生产
代码分析
我们用代码来模拟生产者和消费者之间的生产与消费行为,分析:
- 需要一个数据缓冲队列,缓存队列中的数据大小有上限,只能进行生成和消费两种操作
- 定义一个生产者线程,调用缓存队列的生产行为,需用synchronized限制
- 定义一个消费者线程,调用缓存队列的消费行为,需用synchronized限制
- 调用生产者生产时,如果缓存队列满,则生产线程阻塞进入等待。如果缓存队列未满,则进行具体的生产,方法完毕后释放锁,通知消费者消费
- 调用消费者消费时,如果缓存队列已空,则消费者线程阻塞进入等待。如果缓存队列未满,则进行消费操作,操作完毕释放锁,通知生产者生产。
public class ProducerConsumerTest {
public static void main(String[] args) {
BufferQueue<String> linkedHashQueue = new ReentrantLockQueue<>();
ProducerThread producerThread = new ProducerThread(linkedHashQueue);
ConsumerThread consumerThread = new ConsumerThread(linkedHashQueue);
producerThread.start();//启动生产者线程
consumerThread.start();//启动消费者线程
try {
Thread.sleep(4000);
ProducerThread producerThread2 = new ProducerThread(linkedHashQueue,"追加");
producerThread2.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ProducerThread extends Thread {
private BufferQueue<String> linkedHashQueue3;
private String msg;
public ProducerThread(BufferQueue<String> linkedHashQueue) {
this.linkedHashQueue3 = linkedHashQueue;
}
public ProducerThread(BufferQueue<String> linkedHashQueue, String msg) {
this.linkedHashQueue3 = linkedHashQueue;
this.msg = msg;
}
@Override
public void run() {
for (int i = 0; i < 60; i++) {
linkedHashQueue3.add(msg + "——西瓜_" + i);
}
}
}
public class ConsumerThread extends Thread {
private BufferQueue<String> linkedHashQueue;
public ConsumerThread(BufferQueue<String> linkedHashQueue) {
this.linkedHashQueue = linkedHashQueue;
}
@Override
public void run() {
for (; ; ) {
linkedHashQueue.remove();
}
}
}
public class LinkedHashQueue<T> extends BufferQueue<T> {
/**
* 缓冲区
*/
private LinkedHashMap<Integer, T> linkedHashMap = new LinkedHashMap<>();
/**
* 生产者生产物质
* 1.数据缓冲满,则停止生产
* 2.数据缓冲未满,则唤醒等待队列中的消费线程进行消费,执行完synchronized方法才会释放锁
*
* @param msg 商品名
*/
@Override
public synchronized void add(T msg) {
//如果缓存区达到最大数量,则阻塞生产者线程
if (linkedHashMap.size() == maxCount) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
notifyAll();//唤醒所有线程
}
linkedHashMap.put(putIndex, msg);
System.out.println("生产一个产品,当前商品角标为:" + putIndex + "===文本为:" + msg + "===缓存长度为:" + linkedHashMap.size());
putIndex = (putIndex + 1 >= maxCount) ? (putIndex + 1) % maxCount : putIndex + 1;
}
/**
* 消费者进行消费
* 1.数据缓冲区为0,进入等待状态
* 2.消费消费物质后,
*/
@Override
public synchronized T remove() {
if (linkedHashMap.size() == 0) {
//如果缓存区没有数据,则阻塞消费线程
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
notifyAll();//唤醒所有线程
}
Iterator<Map.Entry<Integer, T>> it = linkedHashMap.entrySet().iterator();
T t = null;
if (it.hasNext()) {
Map.Entry<Integer, T> entry = it.next();
t = entry.getValue();
int index = entry.getKey();
linkedHashMap.remove(index);
System.out.println("消费一个产品,当前商品角标为:" + index + "===文本为:" + t + "===缓存长度为:" + linkedHashMap.size());
}
return t;
}
}
public abstract class BufferQueue<T> {
/**
* 数据插入的角标
*/
protected int putIndex = 0;
/**
* 缓存区最大长度
*/
protected final int maxCount = 20;
abstract void add(T msg);
abstract T remove();
}
对于缓存队列也有另两种方式:
public class LinkedBlockQueue<T> extends BufferQueue<T> {
/**
* 缓冲区
*/
private BlockingDeque<T> blockingDeque = new LinkedBlockingDeque<>(20);
@Override
public void add(T msg) {
try {
blockingDeque.put(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public T remove() {
T t = null;
try {
t = blockingDeque.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return t;
}
}
public class ReentrantLockQueue<T> extends BufferQueue<T> {
/**
* 缓冲区
*/
private LinkedHashMap<Integer, T> linkedHashMap = new LinkedHashMap<>();
private Lock lock;
private Condition addCondition;
private Condition removeCondition;
public ReentrantLockQueue() {
lock = new ReentrantLock();
addCondition = lock.newCondition();
removeCondition = lock.newCondition();
}
@Override
public void add(T msg) {
lock.lock();
try {
if (linkedHashMap.size() == maxCount) {
//如果缓存区达到最大数量,则阻塞生产者线程
addCondition.await();//等待
}
linkedHashMap.put(putIndex, msg);
System.out.println("生产一个产品,当前商品角标为:" + putIndex + "===文本为:" + msg + "===缓存长度为:" + linkedHashMap.size());
putIndex = (putIndex + 1 >= maxCount) ? (putIndex + 1) % maxCount : putIndex + 1;
//唤醒所有线程
removeCondition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
@Override
public T remove() {
T t = null;
lock.lock();
try {
if (linkedHashMap.size() == 0) {
//如果缓存区没有数据,则阻塞消费线程
removeCondition.await();//等待
}
Iterator<Map.Entry<Integer, T>> it = linkedHashMap.entrySet().iterator();
if (it.hasNext()) {
Map.Entry<Integer, T> entry = it.next();
t = entry.getValue();
int index = entry.getKey();
linkedHashMap.remove(index);
System.out.println("消费一个产品,当前商品角标为:" + index + "===文本为:" + t + "===缓存长度为:" + linkedHashMap.size());
}
addCondition.signalAll();//唤醒所有线程
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return t;
}
}
博客书写不易,您的点赞收藏是我前进的动力,千万别忘记点赞、 收藏 ^ _ ^ !
相关链接: