线程小酌之JAVA中的阻塞队列
Posted tanyunlong_nice
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程小酌之JAVA中的阻塞队列相关的知识,希望对你有一定的参考价值。
最近在学习java中自带的JDK并发包,java.util.concurrent,发现功能很强大,其中之一就是工作中多次用到的线程工具类BlockingQueue。在实际开发工作和面试过程中,经常会考察对于该工具类的使用和理解。
1. 什么是阻塞队列?
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。2.详解BlockingQueue
BlockingQueue最终会有四种状况,抛出异常、返回特殊值、阻塞、超时,下表总结了这些方法:
抛出异常 | 特殊值 | 阻塞 | 超时 | |
插入 | add(e) | offer(e) | put(e) | offer(e, time, unit) |
移除 | remove() | poll() | take() | poll(time, unit) |
检查 | element() | peek() | 不可用 | 不可用 |
BlockingQueue是个接口,有如下实现类:
1. ArrayBlockQueue:一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。创建其对象必须明确大小,像数组一样。
2. LinkedBlockQueue:一个可改变大小的阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。创建其对象如果没有明确大小,默认值是Integer.MAX_VALUE。链接队列的吞吐量通常要高于基于数组的队列,但是在大多数并发应用程序中,其可预知的性能要低。
3. PriorityBlockingQueue:类似于LinkedBlockingQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数所带的Comparator决定的顺序。
4. SynchronousQueue:同步队列。同步队列没有任何容量,每个插入必须等待另一个线程移除,反之亦然。
由于LinkedBlockingQueue实现是线程安全的,实现了先进先出等特性,是作为生产者消费者的首选,LinkedBlockingQueue 可以指定容量,也可以不指定,不指定的话,默认最大是Integer.MAX_VALUE,其中主要用到put和take方法,put方法在队列满的时候会阻塞直到有队列成员被消费,take方法在队列空的时候会阻塞,直到有队列成员被放进来。
package cn.thread;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
/**
* 多线程模拟实现生产者/消费者模型
*
*/
public class BlockingQueueTest2
/**
*
* 定义装苹果的篮子
*
*/
public class Basket
// 篮子,能够容纳3个苹果
BlockingQueue<String> basket = new LinkedBlockingQueue<String>(3);
// 生产苹果,放入篮子
public void produce() throws InterruptedException
// put方法放入一个苹果,若basket满了,等到basket有位置
basket.put("An apple");
// 消费苹果,从篮子中取走
public String consume() throws InterruptedException
// take方法取出一个苹果,若basket为空,等到basket有苹果为止(获取并移除此队列的头部)
return basket.take();
// 定义苹果生产者
class Producer implements Runnable
private String instance;
private Basket basket;
public Producer(String instance, Basket basket)
this.instance = instance;
this.basket = basket;
public void run()
try
while (true)
// 生产苹果
System.out.println("生产者准备生产苹果:" + instance);
basket.produce();
System.out.println("!生产者生产苹果完毕:" + instance);
// 休眠300ms
Thread.sleep(300);
catch (InterruptedException ex)
System.out.println("Producer Interrupted");
// 定义苹果消费者
class Consumer implements Runnable
private String instance;
private Basket basket;
public Consumer(String instance, Basket basket)
this.instance = instance;
this.basket = basket;
public void run()
try
while (true)
// 消费苹果
System.out.println("消费者准备消费苹果:" + instance);
System.out.println(basket.consume());
System.out.println("!消费者消费苹果完毕:" + instance);
// 休眠1000ms
Thread.sleep(1000);
catch (InterruptedException ex)
System.out.println("Consumer Interrupted");
public static void main(String[] args)
BlockingQueueTest2 test = new BlockingQueueTest2();
// 建立一个装苹果的篮子
Basket basket = test.new Basket();
ExecutorService service = Executors.newCachedThreadPool();
Producer producer = test.new Producer("生产者001", basket);
Producer producer2 = test.new Producer("生产者002", basket);
Consumer consumer = test.new Consumer("消费者001", basket);
service.submit(producer);
service.submit(producer2);
service.submit(consumer);
// 程序运行5s后,所有任务停止
// try
// Thread.sleep(1000 * 5);
// catch (InterruptedException e)
// e.printStackTrace();
//
// service.shutdownNow();
以上是关于线程小酌之JAVA中的阻塞队列的主要内容,如果未能解决你的问题,请参考以下文章
JUC并发编程 共享模式之工具 线程池 -- 自定义线程池(阻塞队列)
Java 并发编程线程池机制 ( 线程池执行任务细节分析 | 线程池执行 execute 源码分析 | 先创建核心线程 | 再放入阻塞队列 | 最后创建非核心线程 )