阻塞队列
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。
- 支持阻塞的插入方法:当队满时,队列会阻塞插入元素的线程,一直到队列不满。
- 支持阻塞的移出方法:当队列为空时,获取元素的线程会等待队列变为非空。
阻塞队列常用于生产者和消费者的场景,生产者是向队列里添加元素的线程,消费者是从队列里取元素的线程。阻塞队列就是生产者用来存放元素、消费者用来获取元素的容器。
部分方法
| 方法 | 抛出异常 | 返回特殊值 | 一直阻塞 | 超时退出 |
|---|---|---|---|---|
| 插入方法 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
| 移出方法 | remove() | poll() | take() | poll(time,unit) |
| 检查方法 | element() | peek() | 无 | 无 |
- 抛出异常:是指当阻塞队列满时,再往队列里插入元素,会抛出java.lang.IllegalStateException: Queue full 。当队列为空时,从队列里获取元素时,会抛出NoSuchElementException。
- 返回特殊值:插入成功会返回true,失败为false。移除方法:队列有值就返回值,队列空就返回null。
- 一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程。当队列为空时,消费者线程试图从队列里take元素,队列也会阻塞消费者线程,直到队列可用。
- 超时退出:当阻塞队列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出。
package cn.lvhaosir;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
public class Test006 {
public static void main(String[] args) {
ArrayBlockingQueue<String> abq = new ArrayBlockingQueue<String>(4);
/**
* 四种插入方式
*/
// 若队满,抛出异常,java.lang.IllegalStateException: Queue full
boolean add = abq.add("张三");
System.out.println(add);
// 若队满,返回值为:false
boolean offer = abq.offer("李四");
try {
// 若队满,则线程一直阻塞,直到数据插入
abq.put("王五");
// 若队满,线程会阻塞2秒,也可以获取一个返回值
boolean offer2 = abq.offer("老六", 2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 若队空,抛出异常NoSuchElementException
String remove = abq.remove();
// 若队空,返回null
String poll = abq.poll();
try {
// 若队空,一直阻塞
String take = abq.take();
// 若队空,线程会阻塞2秒,获取null或值
String poll2 = abq.poll(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
常用的阻塞队列
ArrayBlockingQueue
- ArrayBlockingQueue是一个有边界的阻塞队列,它的内部实现是一个数组。有边界的意思是它的容量是有限的。我们必须在其初始化的时候执行它的容量大小,容量大小一旦指定就无法改变。ArrayBlockingQueue是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。
LinkedBlockingQueue
- LinkedBlockingQueue阻塞队列大小的配置是可选的,如果我们初始化时指定一个大小,它就是有边界的。如果不指定,它就是无边界的,其实是采用了默认大小为Integer.MAX_VALUE的容量。内部实现是一个链表。和ArrayBlockingQueue一样,LinkedBlockingQueue也是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。
PriorityBlockingQueue
- PriorityBlockingQueue是一个没有边界的队列,它的排序规则和 java.util.PriorityQueue一样,PriorityBlockingQueue中允许插入null对象。所有插入PriorityBlockingQueue的对象必须实现 java.lang.Comparable接口,队列优先级的排序规则就是按照我们对这个接口的实现来定义的。另外,我们可以从PriorityBlockingQueue获得一个迭代器Iterator,但这个迭代器并不保证按照优先级顺序进行迭代。
SynchronousQueue
- SynchronousQueue队列内部仅允许容纳一个元素。当一个线程插入一个元素后会被阻塞,除非这个元素被另一个线程消费。可以认为SynchronousQueue是一个缓存值为1的阻塞队列,但是 isEmpty()方法永远返回是true,remainingCapacity() 方法永远返回是0,remove()和removeAll() 方法永远返回是false,iterator()方法永远返回空,peek()方法永远返回null,size()永远返回0。
常用的非阻塞队列
ConcurrentLinkedDeque
- ConcurrentLinkedDeque是一个适用于高并发场景下的队列,通过无锁的复方石,实现了高并发状态下的高性能,通常ConcurrentLinkedQueue性能好于BlockingQueue,它是一个基于链接节点的无界线程安全队列,该队列的元素遵循先进先出的原则。最新插入的对象是尾部,最新移出的对象是头部,该队列不允许null元素。
使用阻塞队列,模拟生产者消费者
package cn.lvhaosir;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
class ProducerThread extends Thread {
// 阻塞队列
private BlockingQueue<String> blockingQueue;
// 标志
private volatile boolean flag = true;
// 原子数
private static AtomicInteger count = new AtomicInteger();
public ProducerThread(BlockingQueue<String> blockingQueue) {
super();
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
System.out.println("生产者线程启动...");
try {
// 如果 flag 为 true,代表为空,就生产
while(flag) {
String data = String.valueOf(count.getAndIncrement());
// 生产
boolean offer = blockingQueue.offer(data, 2, TimeUnit.SECONDS);
if (offer) {
System.out.println("生产成功,data="+data);
} else {
System.out.println("生产失败...");
}
// 停顿1s
Thread.sleep(1000);
}
} catch (Exception e) {
// TODO: handle exception
} finally {
System.out.println("生产者离职...");
}
}
public void stopThread() {
this.flag = false;
}
}
class ConsumerThread extends Thread {
private BlockingQueue blockingQueue;
private volatile boolean flag = true ;
public ConsumerThread(BlockingQueue blockingQueue) {
super();
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
try {
while(flag) {
// 如果有资源,就去消费
String data = (String) blockingQueue.poll(2, TimeUnit.SECONDS);
if(data != null) {
System.out.println("消费成功,得到数据 data="+data);
} else {
System.out.println("消费失败,没有资源");
this.flag = false;
}
// 停顿1s
// Thread.sleep(1000);
}
} catch (Exception e) {
// TODO: handle exception
} finally {
System.out.println("消费者走了...");
}
}
}
public class Test007 {
public static void main(String[] args) throws InterruptedException {
BlockingQueue blockingQueue = new LinkedBlockingQueue();
ProducerThread p1 = new ProducerThread(blockingQueue);
ProducerThread p2 = new ProducerThread(blockingQueue);
ConsumerThread c1 = new ConsumerThread(blockingQueue);
p1.start();
p2.start();
c1.start();
// 运行 3s
Thread.sleep(3000);
// 结束生产
p1.stopThread();
p2.stopThread();
}
}
浙公网安备 33010602011771号