Java并发编程之阻塞队列
目录
- 阻塞队列概述
- 为什么用?有什么好处?
- BlockingQueue的核心方法
- SynchronousQueue的用法
- 用在哪里
1. 阻塞队列概述
- 阻塞队列,顾名思义,首先它是一个队列,而一个阻塞队列在数据结构中起到的作业大致如下图所示:
- 当线程队列是空时,从队列中获取元素的操作将会被阻塞。
- 试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。
- 同样,试图往已满的阻塞队列中添加新元素的线程也同样会被阻塞,直到其他的线程从列总移除一个或多个元素或者完全清空队列后使队列重新变得空闲起来并后续新增。
2. 为什么用?有什么好处?
- 在多线程领域;所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒
- 为什么需要BlockingQueue?
- 好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你一手包办了
- 在concurrent包发布以前,在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而
这会给我们的程序带来不小的复杂度。
3. BlockingQueue的核心方法
1. 核心方法概述
2. 核心方法api使用
1. 阻塞队列api之抛出异常组
代码:
public class BlockingQueueDemo {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println("添加元素" + blockingQueue.add("a"));
System.out.println("添加元素" + blockingQueue.add("b"));
System.out.println("添加元素" + blockingQueue.add("c"));
// System.out.println("添加元素" + blockingQueue.add("d"));
//检查队列第一个元素
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());
}
}
1.1 正常执行结果:
1.2 将注释的代码System.out.println("添加元素" + blockingQueue.add("d"));
打开。即:
执行结果:
报异常:java.lang.IllegalStateException: Queue full
1.3 将注释的代码System.out.println("移除元素" + blockingQueue.remove());
打开,即:
执行结果:
报异常:Exception in thread “main” java.util.NoSuchElementException
1.4 小结:
- 当阻塞队列满时,再往队列里add插入元素会抛出java.lang.IllegalStateException: Queue full异常。
当阻塞队列空时,再往队列里remove移除元素会抛出Exception in thread “main” java.util.NoSuchElementException异常。
2. 阻塞队列api之返回布尔值组
代码:
public static void main(String[] args) throws InterruptedException {
System.out.println(blockingQueue.offer("a"));
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println("添加元素"+blockingQueue.offer("a"));
System.out.println("添加元素"+blockingQueue.offer("b"));
System.out.println("添加元素"+blockingQueue.offer("c"));
System.out.println("添加元素"+blockingQueue.offer("a"));
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());
2.1 执行结果:
2.2 小结:
- 插入方法,成功ture,失败false
- 移除方法,成功返回出队列的元素,队列里没有就返回null
3. 阻塞队列api之阻塞
代码:
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.put("a");
blockingQueue.put("a");
blockingQueue.put("a");
//blockingQueue.put("a");
System.out.println("==================");
blockingQueue.take();
blockingQueue.take();
blockingQueue.take();
//blockingQueue.take();
}
当打开注释blockingQueue.put("a");
或者blockingQueue.take();
执行结果:
小结:
- 当阻塞队列满时,生产者线程继续往队列里put元素,队列会一直阻塞生产线程直到put数据或者响应中断退出。
- 当阻塞队列空时,消费者线程试图从队列里take元素,队列会一直阻塞消费者线程直到队列可用。
4. 阻塞队列api之超时控制
代码:
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
}
执行结果:
先阻塞两秒钟,2秒后自动结束
4. SynchronousQueue的用法
- 与其他BlockingQueue不同,SynchronousQueue是一个不存储元素的BlockingQueue。每一个put操作必须要等待一个take操作,否则不能继续添加元素,反之亦然。
代码实例:
public static void main(String[] args) throws Exception {
BlockingQueue<String> blockingQueue = new SynchronousQueue<>();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName()+"\t put 1");
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName()+"\t put 2");
blockingQueue.put("2");
System.out.println(Thread.currentThread().getName()+"\t put 3");
blockingQueue.put("3");
}catch (Exception e){
e.printStackTrace();
}
},"AAA").start();
new Thread(() -> {
try {
//暂停一会线程
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());
}catch (Exception e){
e.printStackTrace();
}
},"BBB").start();
}
执行结果:
5. 用在哪里
主要用在:
- 生产者消费者模式(阻塞队列版)
- 线程池
- 消息中间件
等后面写完了再补上链接