1阻塞队列介绍
概念:在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),⼀旦条件满足,被挂起的线程又会自动被唤醒。
阻塞队列 是⼀个队列,在数据结构中起的作用如下图:
当队列是空的,从队列中获取(Take)元素的操作将会被阻塞
当队列是满的,从队列中添加(Put)元素的操作将会被阻塞
试图中空的队列中获取元素的线程将会被阻塞,直到其他线程往空的队列插入新的元素
试图向已满的队列中添加新元素的线程将会被阻塞,直到其他线程从队列中移除⼀个或多个元素或者完全清空,使队列变得空闲起来后并后续新增
好处:阻塞队列不用手动控制什么时候该被阻塞,什么时候该被唤醒,简化了操作。
体系: Collection → Queue → BlockingQueue →七个阻塞队列实现类。
2BlockingQueue 的使用
类名 | 作用 |
ArrayBlockingQueue
|
由数组结构
构成的
有界阻塞队列
|
LinkedBlockingQueue
|
由
链表结构
构成的
有界(但默认值为
Integer.MAX_VALUE
)
阻塞队列
|
PriorityBlockingQueue
|
支持优先级排序的无界阻塞队列
|
DelayQueue
|
使用优先级队列实现的延迟⽆界阻塞队列
|
SynchronousQueue
|
不存储元素的阻塞队列,也即单个元素的队列
|
LinkedTransferQueue
|
由链表构成的⽆界阻塞队列
|
LinkedBlockingDeque
|
由链表构成的双向阻塞队列
|
粗体标记的三个用的比较多,许多消息中间件底层就是⽤它们实现的。需要注意的是 LinkedBlockingQueue 虽然是有界的,但有个巨坑,其默认大小是 Integer.MAX_VALUE ,高达21亿,⼀般情况下内存早爆了(在线程池的 ThreadPoolExecutor 有体现)。
API:
抛出异常是指当队列满时,再次插入会抛出异常;
返回布尔是指当队列满时,再次插⼊会返回false;阻塞是指当队列满时,再次插入会被阻塞,直
到队列取出⼀个元素,才能插入。超时是指当⼀个时限过后,才会插入或者取出。
方法类型
| 抛出异常 | 返回布尔 | 阻塞 | 超时 |
插入
| add(E e) | offer(E e) | put(E e) | offer(E e,Time,TimeUnit) |
取出
| remove() | poll() | take() | poll(Time,TimeUnit) |
队首
|
element()
| peek() | 无 | 无 |
api使用代码
2.1常用方法的使用(ArrayBlockingQueue())
由数组结构构成的有界阻塞队列
测试api运行代码
public class BlockingQueueDemo {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
// addAndRemove(blockingQueue);
// offerAndPoll(blockingQueue);
// putAndTake(blockingQueue);
// outOfTime(blockingQueue);
}
/**
* 超时的方式演示
* @param blockingQueue
*/
private static void outOfTime(BlockingQueue<String> blockingQueue) throws InterruptedException {
System.out.println(blockingQueue.offer("AA", 4, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("BB", 4, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("CC", 4, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("DD", 4, TimeUnit.SECONDS));
//取出队列元素
System.out.println(blockingQueue.poll(4, TimeUnit.SECONDS));
System.out.println(blockingQueue.poll(4, TimeUnit.SECONDS));
System.out.println(blockingQueue.poll(4, TimeUnit.SECONDS));
System.out.println(blockingQueue.poll(4, TimeUnit.SECONDS));
}
/**
* 阻塞形式的方式演示
* @param blockingQueue
*/
private static void putAndTake(BlockingQueue<String> blockingQueue) throws InterruptedException {
blockingQueue.put("aaa");
blockingQueue.put("bbb");
blockingQueue.put("ccc");
// blockingQueue.put("ddd");
//取出队列元素
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
}
/**
* 返回布尔值的方法演示
* @param blockingQueue
*/
private static void offerAndPoll(BlockingQueue<String> blockingQueue) {
System.out.println(blockingQueue.offer("aa"));
System.out.println(blockingQueue.offer("bb"));
System.out.println(blockingQueue.offer("cc"));
System.out.println(blockingQueue.offer("dd"));
//查看队首元素
//System.out.println(blockingQueue.peek());
//取出队列中的元素
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
}
/**
* 抛出异常的方法演示
* @param blockingQueue
*/
private static void addAndRemove(BlockingQueue<String> blockingQueue) {
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
// System.out.println(blockingQueue.add("d"));//不能添加 报Queue full错误,队列已满
//查看队首元素
System.out.println(blockingQueue.element());
//取出队列元素
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());//不能取出因为队列已空 NoSuchElementException
}
}
2.2SynchronousQueue的使用
不存储元素的阻塞队列,也即单个元素的队列,
BlockingQueue<String> blockingQueue = new SynchronousQueue<>();
BlockingQueue<String> arrayBlockingQueue= new ArrayBlockingQueue<>(1);
从代码运行来看,当arrayBlockingQueue的长度设置为1时等同于blockingQueue的数据存储效果
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new SynchronousQueue<>();//从结果看两个方式可以相等
// BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(1);
//商店放入商品,顾客取走商品
new Thread(()->{
try {
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName()+"\t put 1");
blockingQueue.put("2");
System.out.println(Thread.currentThread().getName()+"\t put 2");
blockingQueue.put("3");
System.out.println(Thread.currentThread().getName()+"\t put 3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "商店").start();
new Thread(()->{
try {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"\t take"+blockingQueue.take());
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"\t take"+blockingQueue.take());
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"\t take"+blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "顾客").start();
}