阻塞队列 - BlockingQueue
线程通信的一个工具。在任意时刻,不管并发有多高,在单JVM上面,同一时间永远只有一个线程能够对队列进行入队或者出队操作。
1. 线程安全的队列;
2. 队列类型:无限队列、有限队列;
常见的4种阻塞队列:
- ArrayBlockingQueue 由数组支持的有界队列
- LinkedBlockingQueue 由链接节点支持的可选有界队列
- PriorityBlockingQueue 由优先级堆支持的无解优先级队列
- DelayQueue 由优先级堆支持的、基于时间的调度队列
应用场景:
1. 线程池,SpringCloud-Eureka的三级缓存,Nacos、Netty、MQ(RocketMQ)、生产者与消费者模型
例如ArrayBlockingQueue,其本质内部有一个ReetrantLock,如果线程想要操作这个队列之前,必须先获得这把锁。
在BlockingQue内部执行deque和enque的时候,如果当前队列判了,会有一个await的操作,也就是上述列出的一张图的代码。
在这里面可以看到具体得执行流程。
这里要提出一个等待队列的概念,之前讲AQS的时候只讲到同步队列,同步队列是真正可以拿到cpu去执行线程的队列。而等待队列用于BlockingQueue可以很巧合地实现生产者和消费者之前入队和出队的操作,里面有两个内部变量notFull,notEmpty,有这两个条件变量在,可以很巧合地通知通知机制,消费者消费后去通知生产者进入同步队列执行放入数据到等待队列,生产者生产数据后可以去通知消费者到同步队列执行从等待队列取出数据。总之生产者和消费者总是在等待队列和同步队列之间来回切换。
下面见ArrayBlockingQueue的构造器和put方法。
public ArrayBlockingQueue(int capacity, boolean fair) { if(capacity <= 0){ throw new IllegalArgumentException(); } this.items = new Object[capacity]; lock = new ReentrantLock(fair); // 创建一把锁 notEmpty = lock.newCondition(); // 条件对象 notFull = lock.newCondition(); // 条件对象 } public void put(E e) throws InterruptedException { checkNotNull(e); final ReentrantLock lock = this.lock; // producer加锁 lock.lockInterruptibly(); try { while(count == items.length) notFull.await(); enqueue(e); } finally { lock.unlock(); } }
End!