阻塞队列:应用于多线程环境,
当队列为空时,线程从中取元素会被阻塞并且置于等待状态,直到有线程向其中放入元素;
当队列为满时,向其中放入元素的线程会被阻塞并且置于等待状态,直到有线程取出元素。
ArrayBlockingQueue提供一种有界阻塞队列
PriorityBlockingQueue支持在并发情况下的优先级队列
LinkedBlockingQueue是无界的阻塞队列
LinkedBlockingDeque类提供对双端结点的操作
SynchronousQueue为异步队列,不存储元素
DelayQueue提供一种延时执行任务的队列
以一个生产者消费者的例子体现阻塞队列如何使用:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class TestBlockingQueue {
static BlockingQueue<Person> blockingQueue = new ArrayBlockingQueue<>(10);
public static void main(String[] args) {
new Thread(() -> {
for (int i = 1; i < 100; i++) {
try {
blockingQueue.put(new Person(i));
System.out.println("第"+i+"个大佬进入队列排队");
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"Producer").start();
new Thread(() -> {
for (int i = 1; i < 100; i++) {
Person person = null;
try {
person = blockingQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "-----将第"+person.id+"位大佬带出队列");
}
},"Consumer1").start();
new Thread(() -> {
for (int i = 0; i < 100; i++) {
Person person = null;
try {
person = blockingQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "-----将第"+person.id+"位大佬带出队列");
}
},"Consumer2").start();
new Thread(() -> {
for (int i = 0; i < 100; i++) {
Person person = null;
try {
person = blockingQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "-----将第"+person.id+"位大佬带出队列");
}
},"Consumer3").start();
}
}
class Person{
int id;
public Person(int id){
this.id = id;
}
}
1. 阻塞队列的种类
1.1. ArrayBlockingQueue:
- ArrayBlockingQueue是一个用数组实现的有界阻塞队列。
- 此队列按照先进先出(FIFO)的原则对元素进行排序。
- 默认不保证访问者公平的访问队列,所谓公平访问队列是指阻塞的所有生产者线程或消费者线程,当队列可用时,可以按照阻塞的先后顺序访问队列,即先阻塞的生产者线程,可以先往队列里插入元素,先阻塞的消费者线程,可以先从队列里获取元素。通常情况下为了保证公平性会降低吞吐量。
- 公平性是使用可重入锁实现的。
1.2. LinkedBlockingQueue:
LinkedBlockingQueue是一个用链表实现的有界阻塞队列。此队列的默认和最大长度为Integer.MAX_VALUE。此队列按照先进先出的原则对元素进行排序。
1.3. PriorityBlockingQueue:
一个支持优先级的无界队列。默认情况下元素采取自然顺序排列,也可以通过比较器comparator来指定元素的排序规则。元素按照升序排列。
1.4. DelayQueue
是一个支持延时获取元素的无界阻塞队列。队列使用PriorityQueue来实现。队列中的元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延迟期满时才能从队列中提取元素。我们可以将DelayQueue运用在以下应用场景:
- 缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。
- 定时任务调度。使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,从比如TimerQueue就是使用DelayQueue实现的。
队列中的Delayed必须实现compareTo来指定元素的顺序。比如让延时时间最长的放在队列的末尾。
1.5. SynchronousBlockingQueue
是一个不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。队列本身并不存储任何元素,非常适合于传递性场景,比如在一个线程中使用的数据,传递给另外一个线程使用,SynchronousQueue的吞吐量高于LinkedBlockingQueue 和 ArrayBlockingQueue。
1.6. LinkedTransferQueue
LinkedTransferQueue是一个由链表结构组成的无界阻塞TransferQueue队列。相对于其他阻塞队列,LinkedTransferQueue多了tryTransfer和transfer方法。
- transfer方法。如果当前有消费者正在等待接收元素(消费者使用take()方法或带时间限制的poll()方法时),transfer方法可以把生产者传入的元素立刻transfer(传输)给消费者。如果没有消费者在等待接收元素,transfer方法会将元素存放在队列的tail节点,并等到该元素被消费者消费了才返回。
- tryTransfer方法。则是用来试探下生产者传入的元素是否能直接传给消费者。如果没有消费者等待接收元素,则返回false。和transfer方法的区别是tryTransfer方法无论消费者是否接收,方法立即返回。而transfer方法是必须等到消费者消费了才返回。