阻塞队列的实现原理
1、生产者向队尾添加元素
2、消费者向队头消费元素
3、添加和消费过程是线程安全的
实现一
public class MyBlockingContainer<T> {
private Queue<T> queue=new LinkedList<>();
private final int MAX;
public MyBlockingContainer(int limit){
this.MAX=limit;
}
public void put(T t) throws InterruptedException {
synchronized (this){
while(queue.size()==MAX){
this.wait();//释放锁并且阻塞自己,要想唤醒必须先获取锁
}
queue.offer(t);
this.notifyAll();//唤醒消费者线程,此时还没有释放锁
}
}
public T get() throws InterruptedException {
T t;
synchronized (this){
while(queue.size()==0){
this.wait();
}
t=queue.poll();
this.notifyAll();
}
return t;
}
}
实现一的方法很简单,原理是synchronized+wait+notify实现,但是notify的时候是会唤醒生产者线程和消费者线程的,想象一下,当前生产者线程已经生产了MAX个元素,当他唤醒其他线程的时候,也会唤醒生产者线程,在这里显然是没有必要的。
实现二
public class MyBlockingQueueForLock<T> {
private Queue<T> queue=new LinkedList<>();
private final int MAX;
private ReentrantLock lock=new ReentrantLock();
private Condition producer=lock.newCondition();
private Condition consumer=lock.newCondition();
public MyBlockingQueueForLock(int limit){
this.MAX=limit;
}
public void put(T t) throws InterruptedException {
final ReentrantLock lock=this.lock;
lock.lockInterruptibly();
try {
while(queue.size()==MAX){
producer.await();//响应中断
}
queue.offer(t);
consumer.signalAll();
}finally {
lock.unlock();
}
}
public T get() throws InterruptedException {
final ReentrantLock lock=this.lock;
lock.lockInterruptibly();
T t;
try {
while (queue.size()==0){
consumer.await();//响应中断
}
t=queue.poll();
producer.signalAll();
}finally {
lock.unlock();
}
return t;
}
}
实现二用AQS的条件队列实现了唤醒线程的灵活性,可以说比实现一更进了一步
总结:JDK自带的阻塞队列其实原理和上面差不多,但是实现得更加灵活,支持高并发。比如LinkedBlockingQueue是用了两把不同的锁来实现,之所以可以这样,是因为链表结构