阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
action | Throws exception | Special value | Blocks | Times out |
Insert | add(e) | offer(e) | put(e) | offer(e, time, unit) |
Remove | remove() | poll() | take() | poll(time, unit) |
Examine | element() | peek() |
Throws exception:异常抛出,当队列满时插入元素和当队列空时取元素,这两种情况都会抛异常;
Special value:特殊值返回,元素插入队列失败和获取队列元素失败,这两种情况会返回false,成功时则返回true;
Blocks:一直阻塞,当队列满时,如果生产者线程往队列put元素,会阻塞生产者线程,直到成功或者响应中断,当队列空时,消费者线程从队列里take元素,这时会阻塞线程获取元素,直到获取成功或响应中断。
Times out:超时退出,线程在队列里添加或取出元素失败时,会在一定时间内一直阻塞,直到成功或者超出时间退出。
Java提供的阻塞队列:
- ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
- PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
- DelayQueue:一个使用优先级队列实现的无界阻塞队列。
- SynchronousQueue:一个不无缓冲的阻塞队列。
- LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
- LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
代码示例:
BlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
new Thread(() -> {
try {
arrayBlockingQueue.put("a");
System.out.println(Thread.currentThread().getName() + "\t a 进入队列");
arrayBlockingQueue.put("b");
System.out.println(Thread.currentThread().getName() + "\t b 进入队列");
arrayBlockingQueue.put("c");
System.out.println(Thread.currentThread().getName() + "\t c 进入队列");
arrayBlockingQueue.put("d");
System.out.println(Thread.currentThread().getName() + "\t d 进入队列");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"AAA").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "\t 取出队列的一个值:" + arrayBlockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"BBB").start();
运行结果:
当队列满,生产者线程无法继续往队列添加,只有消费者线程消费了队列的元素,生产者线程才能继续在队列里添加元素。
BlockingQueue<String> arrayBlockingQueue = new SynchronousQueue<>();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "\t a 进入队列");
arrayBlockingQueue.put("a");
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "\t b 进入队列");
arrayBlockingQueue.put("b");
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "\t c 进入队列");
arrayBlockingQueue.put("c");
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "\t d 进入队列");
arrayBlockingQueue.put("d");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"AAA").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "\t 取出队列的一个值:" + arrayBlockingQueue.take());
System.out.println(Thread.currentThread().getName() + "\t 取出队列的一个值:" + arrayBlockingQueue.take());
System.out.println(Thread.currentThread().getName() + "\t 取出队列的一个值:" + arrayBlockingQueue.take());
System.out.println(Thread.currentThread().getName() + "\t 取出队列的一个值:" + arrayBlockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"BBB").start();
运行结果:
SynchronousQueue可以看做队列大小为1的阻塞队列,添加一个元素后线程无法继续往里添加元素,只有队列里的元素被消费后才能继续添加。