JUC并发编程_阻塞队列 BlockingQueue
- 一、基本概念
- 二、主要特性
- 三、常用方法
- 四、实现类
- ArrayBlockingQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- SynchronousQueue
- 五、使用场景
- 六、注意事项
一、基本概念
阻塞队列是一种特殊的队列,它除了支持普通队列的插入(put/offer)和移除(take/poll)操作外,还支持两个附加的阻塞操作:
当队列满时,如果线程尝试向队列中添加元素,该线程将被阻塞,直到队列中有空间可用。
当队列空时,如果线程尝试从队列中移除元素,该线程将被阻塞,直到队列中有元素可取。
二、主要特性
线程安全
:阻塞队列是线程安全的,多个线程可以并发访问它而不会发生冲突。
生产者-消费者模式:阻塞队列支持生产者-消费者模式,即生产者向队列中添加元素,消费者从队列中取出元素。
阻塞等待
:通过阻塞等待机制,阻塞队列能够自动平衡生产者和消费者的速度差异,防止生产者过快导致队列溢出,或消费者过慢导致队列空置。
三、常用方法
put(E e)
:向队列中添加元素,如果队列已满则阻塞等待。
take()
:从队列中取元素,如果队列为空则阻塞等待。
boolean offer(E e, long timeout, TimeUnit unit)
:向队列中添加元素,如果队列已满,则等待指定的时间。
poll(long timeout, TimeUnit unit)
:从队列中取元素,如果队列为空则等待指定的时间。
四、实现类
Java中常用的阻塞队列的实现类:
ArrayBlockingQueue
基于数组的有界阻塞队列,按照先进先出(FIFO)的原则对元素进行排序。
// 指定容量为 10 的 ArrayBlockingQueue
ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
LinkedBlockingQueue
基于链表的无界阻塞队列(在指定容量时也可以作为有界队列),按照先进先出的顺序进行排序。
// 指定容量为 10 的 LinkedBlockingQueue
BlockingQueue<Integer> queue1 = new LinkedBlockingQueue<>(10);
// 无界阻塞队列
BlockingQueue<Integer> queue2 = new LinkedBlockingQueue<>();
PriorityBlockingQueue
支持优先级的无界阻塞队列,根据元素的优先级顺序进行排序。
SynchronousQueue
同步队列,不存储元素(容量为0),每次插入操作必须等待另一个线程的移除操作,每次移除操作必须等待另一个线程的插入操作。
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
public class SynchronousQueueDemo {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new SynchronousQueue<>(); //同步队列
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+" put 1");
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName()+" put 2");
blockingQueue.put("2");
System.out.println(Thread.currentThread().getName()+" put 3");
blockingQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T1").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T2").start();
}
}
五、使用场景
线程池
:Java中的线程池使用了阻塞队列来管理任务队列,当线程池中的线程数达到最大值时,新的任务会被放入阻塞队列中等待执行。
生产者-消费者模式
:阻塞队列可以非常方便地实现生产者-消费者模式,生产者向队列中添加数据,消费者从队列中取出数据。
消息队列
:阻塞队列可以用于实现消息队列,如Java消息服务(JMS)中的队列和主题就是基于阻塞队列实现的。
多线程协作:阻塞队列可以用于多线程之间的协作,通过阻塞等待机制实现线程间的同步和协调。
六、注意事项
容量设置
:阻塞队列的容量需要根据实际情况进行设置,过小会导致队列溢出,过大会浪费内存资源。
阻塞方法
:put()和take()方法是阻塞的,需要在多线程环境下使用,否则会导致线程阻塞。
公平性
与 非公平性
:某些阻塞队列实现(如ArrayBlockingQueue)支持公平锁和非公平锁,可以根据需要选择合适的锁策略。