ReentrantLock 配合Condition实现一个简单的生产者-消费者阻塞队列
相关类和方法介绍
ReentrantLock
它是一种高级的互斥锁,允许同一个线程多次获取锁。这意味着线程可以在持有锁的情况下,再次获取该锁而不会造成死锁。ReentrantLock保持了锁的公平性和可重入性,因此可以更灵活地控制多线程之间的访问顺序
Condition
Condition是与ReentrantLock相关联的条件对象。它可以让线程在某个条件满足时等待,或者在满足条件时通知其他等待的线程继续执行。一个ReentrantLock可以有多个Condition对象,用于不同的等待和通知场景。
await
Condition提供了await()方法,用于使当前线程等待某个条件满足。当线程调用await()方法时,它会释放当前持有的锁,并进入等待状态,直到其他线程通过signal()或signalAll()方法通知该条件。
signal
signal()和signalAll()方法, 这两个方法用于唤醒等待该条件的线程。signal()方法只会唤醒等待队列中的一个线程,而signalAll()方法会唤醒等待队列中的所有线程。被唤醒的线程会尝试重新获取锁,并继续执行。
问题分析
设计一个阻塞的集合队列
如果集合为空,那么当一个线程调用get时,阻塞该线程,直到有线程生产
如果集合满了,那么当一个线程调用set时,阻塞该线程,直到被线程消费
实现思路
Condition 作为与ReentrantLock相关联的条件对象,可以在满足某个条件的情况下对线程进行阻塞和唤醒某个线程。根据在操作系统中学习过的生产者-消费者模型,设置full和empty两个条件,当full后,阻塞当前的生产者进程,等待一个消费者进程。当empty后,阻塞当前的消费者进程,等待一个生产者进程。
实现类
public class ArrayBlockingQueue<T> {
private final ReentrantLock lock = new ReentrantLock();
private final T[] nodes;
private int count;
private int front = 0;
private int back = 0;
private int maxSize;
private Condition full = lock.newCondition();
private Condition empty = lock.newCondition();
ArrayBlockingQueue(){
this(8);
}
ArrayBlockingQueue(int maxSize){
this.maxSize = maxSize;
this.nodes = (T[]) new Object[maxSize];
}
public T get() throws InterruptedException {
T data;
try {
lock.lock();
while(count == 0){
System.out.println("资源不够,消费者进程阻塞");
empty.await();
}
data = nodes[(back + 1) % maxSize];
full.signal();
count++;
} finally {
lock.unlock();
}
return data;
}
public void set(T node) throws InterruptedException {
try {
lock.lock();
while(count == maxSize){
System.out.println("空间不够,生产者进程阻塞");
full.await();
}
nodes[(front + 1) % maxSize] = node;
empty.signal();
count++;
} finally {
lock.unlock();
}
}
}
测试
public class ArrayTest {
@AllArgsConstructor
static class getThread extends Thread{
private ArrayBlockingQueue queue;
@Override
public void run() {
try {
System.out.println(queue.get());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
@AllArgsConstructor
static class setThread extends Thread{
private ArrayBlockingQueue queue;
@Override
public void run() {
try {
queue.set("hello");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) {
ArrayBlockingQueue queue = new ArrayBlockingQueue();
new getThread(queue).start();
new setThread(queue).start();
new setThread(queue).start();
new setThread(queue).start();
new setThread(queue).start();
new getThread(queue).start();
new setThread(queue).start();
new getThread(queue).start();
new setThread(queue).start();
}
}