目录
BlockingQueue介绍
阻塞队列,并发安全(ReentrantLock),不能插入null
BlockingQueue接口结构
定义了阻塞队列的几种插入和获取元素等方法
操作 | 抛异常 | 特殊值 | 阻塞 | 超时 |
---|---|---|---|---|
插入 | add(E e) | offer(E e) | put(E e) | offer(E e, long timeout, TimeUnit unit) |
移除 | remove() | poll() | take() | poll(long timeout, TimeUnit unit) |
检查 | element() | peek() | - | - |
子类实现基本也是这些方法,只是数据结构不一样.
实现类介绍
实现类 | 介绍 |
---|---|
ArrayBlockingQueue | 内部基于数组实现,一个put索引,一个task索引 ,内部锁(ReentrantLock)可设置公平与非公平 |
LinkedBlockingQueue | 内部链表实现,一个head,一个last,内部非公平锁(ReentrantLock),可指定大小,不指定Integer最大值 |
LinkedBlockingDeque | 双向队列,基于链表实现,内部一个ReentrantLock锁 |
PriorityBlockingQueue | 内部基于数组实现,默认大小11,自动扩容(Integer最大值-8 某些虚拟机头信息),内部非公平锁(ReentrantLock),compareTo()实现取数据顺序,同优先级顺序不确定 |
SynchronousQueue | 内部没有维护容器,使用队列必须绑定一个task,这样put才会成功,无锁采用cas |
DelayQueue | 基于PriorityBlockingQueue实现的延迟队列(无界),元素需实现Delay接口,指定过期时间 |
LinkedTransferQueue | 比一般的阻塞队列多了tryTransfer和transfer方法预占位 |
ArrayBlockingQueue
基于数组实现,维护一个put索引和一个task索引, 先进先出. 可做有界缓冲区
//3种构造方法
@Test
void constructorDemo(){
//队列大小100,默认使用非公平锁
ArrayBlockingQueue<Integer> arrayBlockingQueue1 = new ArrayBlockingQueue(100);
//队列大小100,并且指定锁的是公平还是非给公平. true公平 false非公平
ArrayBlockingQueue<Integer> arrayBlockingQueue2 = new ArrayBlockingQueue(100,true);
//大小100,内部使用非公平锁, 并且以collection初始化ArrayBlockingQueue
Collection<Integer> collection = new ArrayList();
collection.add(1);
ArrayBlockingQueue<Integer> arrayBlockingQueue3 = new ArrayBlockingQueue(100,false,collection);
}
LinkedBlockingQueue
基于链表实现的有界阻塞队列,一个head,一个last,内部非公平锁. 可定义大小,不定义的话Integer最大值,先进先出
//3种构造方法
@Test
void constructorDemo(){
//创建一个容量为 Integer.MAX_VALUE 的 LinkedBlockingQueue。
LinkedBlockingQueue<Integer> linkedBlockingQueue1 = new LinkedBlockingQueue<>();
//创建一个具有给定(固定)容量的 LinkedBlockingQueue。
LinkedBlockingQueue linkedBlockingQueue2 = new LinkedBlockingQueue(10);
Collection<Integer> collection = new ArrayList();
collection.add(1);
//创建一个容量是 Integer.MAX_VALUE 的 LinkedBlockingQueue,最初包含给定 collection 的元素,元素按该 collection 迭代器的遍历顺序添加。
LinkedBlockingQueue linkedBlockingQueue3 = new LinkedBlockingQueue(collection);
}
LinkedBlockingDeque
双向阻塞队列,基于链表,链表默认容量Integer.Max ,可设定大小 ,内部ReentrantLock锁, 维护first,last.,可以当队列也用,可以拿来当栈用
public static void main(String[] args) throws InterruptedException {
LinkedBlockingDeque linkedBlockingDeque = new LinkedBlockingDeque();
linkedBlockingDeque.putLast(0);
linkedBlockingDeque.addFirst(1);
//put -> putLast
linkedBlockingDeque.put(2);
System.out.println(linkedBlockingDeque);
System.out.println("takeFirst " + linkedBlockingDeque.takeFirst());
System.out.println("takeLast " + linkedBlockingDeque.takeLast());
linkedBlockingDeque.push(3);
linkedBlockingDeque.push(4);
System.out.println(linkedBlockingDeque);
System.out.println("pop " + linkedBlockingDeque.pop());
}
PriorityBlockingQueue
内部基于数组实现,默认大小11,自动扩容(Integer最大值-8 某些虚拟机头信息怕报OutOfMemoryError),内部非公平锁, 每次获取元素通过compareTo()实现取元素顺序,同优先级顺序不确定
/**
* @Author: xc
* @Date: 2020/6/12
* 优先级队列
* 通过poll ,task 才有优先级获取元素. 同优先级顺序不确定 (遍历比较 compareTo), 自动扩容
* 一个无界阻塞队列,它使用与类 PriorityQueue 相同的顺序规则,并且提供了阻塞获取操作。
* 虽然此队列逻辑上是无界的,但是资源被耗尽时试图执行 add 操作也将失败(导致 OutOfMemoryError)。
* 此类不允许使用 null 元素。依赖自然顺序的优先级队列也不允许插入不可比较的对象(这样做会导致抛出 ClassCastException)。
*
* 此类及其迭代器可以实现 Collection 和 Iterator 接口的所有可选 方法。
* iterator() 方法中提供的迭代器并不 保证以特定的顺序遍历 PriorityBlockingQueue 的元素。
* 如果需要有序地进行遍历,则应考虑使用 Arrays.sort(pq.toArray())。
* 此外,可以使用方法 drainTo 按优先级顺序移除 全部或部分元素,并将它们放在另一个 collection 中。
*
* 在此类上进行的操作不保证具有同等优先级的元素的顺序。
* 如果需要实施某一排序,那么可以定义自定义类或者比较器,比较器可使用修改键断开主优先级值之间的联系。
* 例如,以下是应用先进先出 (first-in-first-out) 规则断开可比较元素之间联系的一个类。要使用该类,
* 则需要插入一个新的 FIFOEntry(anEntry) 来替换普通的条目对象。
*/
//4种构造方法
@Test
void constructorDemo(){
//用默认的初始容量 (11) 创建一个 PriorityBlockingQueue,并根据元素的自然顺序对其元素进行排序。
PriorityBlockingQueue<Integer> PriorityBlockingQueue1 = new PriorityBlockingQueue<>();
//使用指定的初始容量创建一个 PriorityBlockingQueue,并根据元素的自然顺序对其元素进行排序。
PriorityBlockingQueue<Integer> PriorityBlockingQueue2 = new PriorityBlockingQueue<>(10);
Collection<Integer> collection = new ArrayList();
collection.add(1);
//创建一个包含指定 collection 元素的 PriorityBlockingQueue。
PriorityBlockingQueue<Integer> PriorityBlockingQueue3 = new PriorityBlockingQueue<>(collection);
//使用指定的初始容量创建一个 PriorityBlockingQueue,并根据指定的比较器对其元素进行排序。
PriorityBlockingQueue<Integer> PriorityBlockingQueue4 = new PriorityBlockingQueue<Integer>(10 , (a, b)-> {return a.compareTo(b);} );
}
DelayQueue
基于PriorityBlockingQueue实现的延迟队列,无界
队列元素需实现Delayed接口,延时时间到才能取出元素.
public class DelayQueueDemo {
//两种构造方法
@Test
void constructorDemo(){
//创建一个容量为 Integer.MAX_VALUE 的 DelayQueue。
DelayQueue<Message> delayQueue1 = new DelayQueue<>();
Collection<Message> collection = new ArrayList();
collection.add(new Message(10L,"消息内容"));
//创建一个容量为 Integer.MAX_VALUE 的 DelayQueue,最初包含给定 collection 的元素,以该 collection 迭代器的遍历顺序添加。
LinkedBlockingDeque<Message> delayQueue2 = new LinkedBlockingDeque(collection);
}
public static void main(String[] args) throws InterruptedException {
DelayQueue<Message> queue = new DelayQueue<>();
queue.offer(new Message(100L,"消息1"));
queue.offer(new Message(3000L,"消息2"));
queue.offer(new Message(40L,"消息3"));
queue.offer(new Message(150L,"消息4"));
queue.offer(new Message(30L,"消息5"));
for (;queue.size()>0;){
System.out.println(queue.take());
}
}
}
//延迟队列元素实现Delayed
class Message implements Delayed {
private Long expire;
private Long delayTime;
private String content;
public Long getExpire() {
return expire;
}
public Message(Long delayTime, String content) {
this.delayTime = delayTime;
this.content = content;
this.expire = delayTime + System.currentTimeMillis();
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.expire-System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
return (int)(this.expire - ((Message)o).getExpire());
}
@Override
public String toString() {
return "Message{" +
"expire=" + expire +
", delayTime=" + delayTime +
", content='" + content + '\'' +
'}';
}
}
SynchronousQueue
内部没有维护容器, 采用cas保证安全. 使用SynchronousQueue必须绑定一个消费者,这样生产者才能插入成功
默认非公平 ,可以设置公平
//SynchronousQueue队列 必须绑定一个消费者, 生产才会成功
public static void main(String[] args) throws InterruptedException {
//默认非公平,设置成公平模式
SynchronousQueue synchronousQueue = new SynchronousQueue();
for (int i = 0; i < 5; i++) {
final int index = i;
new Thread(()->{
try {
System.out.println(Thread.currentThread() + " 生产产品"+ index );
synchronousQueue.put(index);
System.out.println(Thread.currentThread() + " 的产品"+ index +"已被消费");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
Thread.sleep(100L);
new Thread(()->{
try {
for (int i = 0; i < 5; i++) {
System.out.println("消费产品: " + synchronousQueue.take());
Thread.sleep(10L);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
LinkedTransferQueue
相对于一般的队列多了transfer和tryTransfer ,这两个方法和SynchronousQueue有点相似
public static void main(String[] args) throws InterruptedException {
LinkedTransferQueue<Integer> linkedTransferQueue = new LinkedTransferQueue();
for (int i = 0; i < 3 ; i++) {
new Thread(()->{
try {
System.out.println(linkedTransferQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
Thread.sleep(100L);
// linkedTransferQueue.add(0);
// 如果至少有一位消费者在等待,则返回 true
System.out.println("hasWaitingConsumer " + linkedTransferQueue.hasWaitingConsumer());
//-阻塞
// 返回等待消费者人数的估计值
System.out.println("hasWaitingConsumer " + linkedTransferQueue.getWaitingConsumerCount());
// 如果有线程在task或者poll,就直接返回,否者 阻塞
linkedTransferQueue.transfer(1);
//相对于transfer 就是不会阻塞,直接返回 false
linkedTransferQueue.tryTransfer(2);
//相对于tryTransfer 在上一个等待
linkedTransferQueue.tryTransfer(3, 1L, TimeUnit.SECONDS);
总结
1.阻塞队列基本实现方法
3种插入 add,offer,put
3种移除 remove, poll,take
遵循: 抛异常,立即返回(和有限时间等待),阻塞
2.不能插入null
3.线程安全保障: ReentrantLock
4.使用最好设定大小: 注意容量,不然出点意外内存就给你爆了
本篇只介绍了阻塞队列基础概念,实现类的原理, 数据结构, 固定方法,具体实现类方法没有说明,也比较简单,可以通过jdk文档,写下demo就基本明了了,我的demo代码:github地址.