并发队列
ConcurrentLinkedQueue
ConcurrentLinkedQueue并发无阻塞队列,BlockingQueue并发阻塞队列,均实现自Queue接口
ConcurrentLinkedQueue无阻塞、无锁、高性能、无界、线程安全,性能优于BlockingQueue、不允许null值
/**
* ConcurrentLinkedQueue
* 无阻赛、无锁、高性能、无界队列(直至内存耗尽)、线程安全,性能优于BlockingQueue、不允许null值
* 使用CAS算法进行入队和出队操作
*/
public class DemoThread29 {
public static void main(String[] args) {
ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<Integer>();
queue.add(1);
queue.add(2); //add方法实际调用了offer方法
//offer方法与add没有区别
queue.offer(3);
queue.offer(4);
//queue.add(null); //不允许添加null元素
System.out.println(queue);
System.out.println("[1]peek="+queue.peek()); //读取头元素,但是不移除
System.out.println("[2]size="+queue.size()); //peek方法不会导致size改变
System.out.println("[3]poll="+queue.poll()); //读取头元素,并且移除
System.out.println("[4]size="+queue.size()); //poll方法导致size改变
System.out.println("[5]poll="+queue.poll());
System.out.println("[6]poll="+queue.poll());
System.out.println("[7]poll="+queue.poll());
System.out.println("[8]size="+queue.size());
System.out.println("peek="+queue.peek()); //队列为空, 读取头元素,返回null
System.out.println("pool="+queue.poll()); //队列为空, 读取头元素并移除, 返回null
}
}
运行结果
[1, 2, 3, 4]
[1]peek=1
[2]size=4
[3]poll=1
[4]size=3
[5]poll=2
[6]poll=3
[7]poll=4
[8]size=0
peek=null
pool=null
ArrayBlockingQueue
ArrayBlockingQueue:基于数组实现的阻塞有界队列、创建时可指定长度,内部实现维护了一个定
长数组用于缓存数据,内部没有采用读写分离,写入和读取数据不能同时进行(COW容器读写可同时进行),不允许null值
采用数组存储
/** The queued items */
final Object[] items;
查看add方法
/**
* Inserts the specified element at the tail of this queue if it is
* possible to do so immediately without exceeding the queue's capacity,
* returning {@code true} upon success and throwing an
* {@code IllegalStateException} if this queue is full.
*
* @param e the element to add
* @return {@code true} (as specified by {@link Collection#add})
* @throws IllegalStateException if this queue is full
* @throws NullPointerException if the specified element is null
*/
public boolean add(E e) {
return super.add(e);
}
调用了父类的add方法
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
调用了offer方法
/**
* Inserts the specified element into this queue if it is possible to do
* so immediately without violating capacity restrictions.
* When using a capacity-restricted queue, this method is generally
* preferable to {@link #add}, which can fail to insert an element only
* by throwing an exception.
*
* @param e the element to add
* @return {@code true} if the element was added to this queue, else
* {@code false}
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this queue
* @throws NullPointerException if the specified element is null and
* this queue does not permit null elements
* @throws IllegalArgumentException if some property of this element
* prevents it from being added to this queue
*/
boolean offer(E e);
查看ArrayBlockingQueue实现的方法,使用ReentrantLock(重入锁)控制线程安全
/**
* Inserts the specified element at the tail of this queue if it is
* possible to do so immediately without exceeding the queue's capacity,
* returning {@code true} upon success and {@code false} if this queue
* is full. This method is generally preferable to method {@link #add},
* which can fail to insert an element only by throwing an exception.
*
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
使用
/**
* BlockingQueue的实现类之ArrayBlockingQueue
* 基于数组实现的阻塞有界队列、创建时可指定长度,内部实现维护了一个定长数组用于缓存数据,
* 内部没有采用读写分离,add和poll数据不能同时进行,可以指定先进先出或后进先出。 使用ReentrantLock(重入锁)控制线程安全
* ---------------------------------------------------
* offer 如果队列已经满了,则不阻塞,不抛出异常
* offer 可设置最大阻塞时间,2秒,如果队列还是满的,则不阻塞,不抛出异常
* add 如果队列满了,则不阻塞,直接抛出异常
* put 如果队列满了,则永远阻塞, 不抛出异常
* ---------------------------------------------------
* peek 读取头元素不移除,队列为空,返回null,不阻塞, 不抛异常
* poll 读取头元素并移除,队列为空,返回null,不阻塞, 不抛异常
* poll 可指定阻塞时间,2秒,如果队列依然为空,则返回null,不抛异常
* take 读取头元素并移除,如果队列为空,则永远阻塞,不抛出异常
* drainTo 取出queue中指定个数(或全部)的元素放入list中,并移除,当队列为空时不抛出异常
*/
public class DemoThread30 {
// 测试各种添加元素的方法
public static void testAdd() {
ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(3);
queue.add(1);
queue.offer(2);
// try {
// queue.add(null); //不允许添加null元素
// } catch (Exception e1) {
// System.out.println("queue.add(null)异常"+e1.getMessage());
// }
try {
queue.put(3);
} catch (InterruptedException e2) {
e2.printStackTrace();
}
try {
queue.add(4); // 如果队列满了,则抛出异常
} catch (Exception e1) {
System.out.println("queue.add(4)异常"+e1.getMessage());
}
System.out.println("1>>" + queue);
queue.offer(4); // 如果队列已经满了,则不阻塞,不抛出异常
System.out.println("queue.offer(4)>>" + queue);
try {
// 可设置最大阻塞时间,5秒,如果队列还是满的,则不阻塞,不抛出异常
queue.offer(6, 2, TimeUnit.SECONDS);
System.out.println("queue.offer(6, 2, TimeUnit.SECONDS)>>" + queue);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
queue.put(7); // 如果队列满了,则永远阻塞, 不抛出异常
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(">>" + queue);
}
// 测试获取数据
public static void testTake1() {
ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(2);
queue.add(1);
queue.add(2);
System.out.println("1>>" + queue.peek()); // 读取头元素不移除
System.out.println(queue);
System.out.println("2>>" + queue.poll()); // 读取头元素,并移除
System.out.println("3>>" + queue);
try {
// 获取头元素,并移除数据
System.out.println("4>>" + queue.take());
System.out.println("5>>" + queue);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("6>>" + queue.peek()); // 队列为空,返回null,不阻塞, 不抛异常
System.out.println("7>>" + queue.poll()); // 队列为空,返回null,不阻塞, 不抛异常
try {
// 可指定阻塞时间,2秒,如果队列依然为空,则返回null,不抛异常
System.out.println("8>>" + queue.poll(2, TimeUnit.SECONDS));
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
queue.take(); // 如果队列为空,则永远阻塞,不抛出异常
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("9>>over");
}
// 测试获取数据2
public static void testTake2() {
ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(3);
queue.add(1);
queue.add(2);
queue.add(3);
ArrayList<Integer> list = new ArrayList<Integer>();
// 英文 drain 喝光,喝干; 使(精力、金钱等)耗尽; 使流出; 排掉水;
queue.drainTo(list, 2); // 取出queue中指定个数的元素放入list中,并移除
System.out.println(list);
System.out.println(queue);
}
// 测试获取数据3
public static void testTake3() {
ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(3);
queue.add(1);
queue.add(2);
queue.add(3);
ArrayList<Integer> list = new ArrayList<Integer>();
queue.drainTo(list); // 取出queue中的全部元素放入list中,并移除
System.out.println("1>>" + list);
System.out.println("2>>" + queue);
ArrayList<Integer> list1 = new ArrayList<Integer>();
queue.drainTo(list1); // 当队列为空时不抛出异常
System.out.println("3>>" + list1);
System.out.println("4>>" + queue);
}
public static void main(String[] args) {
testAdd();
// testTake1();
// testTake2();
// testTake3();
}
}
testAdd测试结果,测试队列满了之后使用各个函数添加数据
queue.add(4)异常Queue full
1>>[1, 2, 3]
queue.offer(4)>>[1, 2, 3]
queue.offer(6, 2, TimeUnit.SECONDS)>>[1, 2, 3]
testTake1测试结果
1>>1
[1, 2]
2>>1
3>>[2]
4>>2
5>>[]
6>>null
7>>null
8>>null
testTake2测试结果,将数据转移到另一个容器
[1, 2]
[3]
drainTo的时候队列为空,不会阻塞,也不会抛出异常
1>>[1, 2, 3]
2>>[]
3>>[]
4>>[]
LinkedBlockingQueue
LinkedBlockingQueue :基于链表的阻塞队列,内部维护一个链表存储缓存数据, 支持写入和读取
的并发操作, 创建时可指定长度也可以不指定,不指定时代表无界队列, 不允许null值
使用ReentrantLock(重入锁)控制线程安全
不允许插入null
/**
* BlockingQueue的实现类之LinkedBlockingDeque
* 基于链表的阻塞队列,内部维护一个链表存储缓存数据
* 内部采用读写分离的锁机制,所以支持写入和读取的并发操作
* 创建时可指定长度也可以不指定,不指定时代表无界队列
* 不允许null值
* ---------------------------------------------------
* offer 如果队列已经满了,则不阻塞,不抛出异常
* offer 可设置最大阻塞时间,2秒,如果队列还是满的,则不阻塞,不抛出异常
* add 如果队列满了,则不阻塞,直接抛出异常
* put 如果队列满了,则永远阻塞, 不抛出异常
* ---------------------------------------------------
* peek 读取头元素不移除,队列为空,返回null,不阻塞, 不抛异常
* poll 读取头元素并移除,队列为空,返回null,不阻塞, 不抛异常
* poll 可指定阻塞时间,2秒,如果队列依然为空,则返回null,不抛异常
* take 读取头元素并移除,如果队列为空,则永远阻塞,不抛出异常
* drainTo 取出queue中指定个数(或全部)的元素放入list中,并移除,当队列为空时不抛出异常
*/
public class DemoThread31 {
// 测试各种添加元素的方法
public static void testAdd() {
LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(3);
queue.add(1);
queue.offer(2);
//queue.add(null); //不允许添加null元素
try {
queue.put(3);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("1>>"+queue);
queue.offer(4); // 如果队列已经满了,则不阻塞,不抛出异常
System.out.println("2>>"+queue);
try {
// 可设置最大阻塞时间,2秒,如果队列还是满的,则不阻塞,不抛出异常
queue.offer(6, 2, TimeUnit.SECONDS);
System.out.println("3>>"+queue);
} catch (InterruptedException e) {
e.printStackTrace();
}
queue.add(7); //如果队列满了,则不阻塞,直接抛出异常
// System.out.println(queue);
try {
queue.put(7); // 如果队列满了,则永远阻塞, 不抛出异常
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("4>>"+queue);
}
// 测试获取数据1
public static void testTake1() {
LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(2);
queue.add(1);
queue.add(2);
System.out.println("1>>"+queue.peek()); // 读取头元素不移除
System.out.println("2>>"+queue);
System.out.println("3>>"+queue.poll()); // 读取头元素,并移除
System.out.println("4>>"+queue);
try {
// 获取头元素,并移除数据
System.out.println("5>>"+queue.take());
System.out.println("6>>"+queue);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("7>>"+queue.peek()); // 队列为空,返回null,不阻塞, 不抛异常
System.out.println("8>>"+queue.poll()); // 队列为空,返回null,不阻塞, 不抛异常
try {
// 可指定阻塞时间,2秒,如果队列依然为空,则返回null,不抛异常
System.out.println("9>>"+queue.poll(2, TimeUnit.SECONDS));
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
queue.take(); // 如果队列为空,则永远阻塞,不抛出异常
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 测试获取数据2
public static void testTake2() {
LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();
queue.add(1);
queue.add(2);
queue.add(3);
ArrayList<Integer> list = new ArrayList<Integer>();
queue.drainTo(list, 2); //取出queue中指定个数的元素放入list中,并移除
System.out.println(list);
System.out.println(queue);
}
// 测试获取数据3
public static void testTake3() {
LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(3);
queue.add(1);
queue.add(2);
queue.add(3);
ArrayList<Integer> list = new ArrayList<Integer>();
queue.drainTo(list); //取出queue中的全部元素放入list中,并移除
System.out.println(list);
System.out.println(queue);
ArrayList<Integer> list1 = new ArrayList<Integer>();
queue.drainTo(list1); //当队列为空时不抛出异常
System.out.println(list1);
System.out.println(queue);
}
public static void main(String[] args) {
testAdd();
// testTake1();
// testTake2();
// testTake3();
}
}
执行testAdd方法,LinkedBlockingQueue满了执行add不阻塞,直接抛出异常
1>>[1, 2, 3]
2>>[1, 2, 3]
3>>[1, 2, 3]
Exception in thread "main" java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(AbstractQueue.java:98)
at com.mimaxueyuan.demo.middle.DemoThread31.testAdd(DemoThread31.java:66)
at com.mimaxueyuan.demo.middle.DemoThread31.main(DemoThread31.java:148)
不指定初始容量,就是无界的,执行结果如下
1>>[1, 2, 3]
2>>[1, 2, 3, 4]
3>>[1, 2, 3, 4, 6]
4>>[1, 2, 3, 4, 6, 7, 7]
执行testTake1结果如下
1>>1
2>>[1, 2]
3>>1
4>>[2]
5>>2
6>>[]
7>>null
8>>null
9>>null
执行testTake2,测试引流
[1, 2]
[3]
执行testTake3,测试引流队列为空的情况,不抛出异常
[1, 2, 3]
[]
[]
[]
SynchronousQueue
SynchronousQueue :没有任何容量,必须现有线程先从队列中take,才能向queue中add数据,否
则会抛出队列已满的异常。不能使用peek方法取数据,此方法底层没有实现,会直接返回null
不允许null
优点:准确高效地在线程之间传递数据,由于是没有容量的,像一个标记或者传送门
举例:t2线程向SynchronousQueue take数据,队列为空就被阻塞了,t1线程向SynchronousQueue加入数据的时候没有经过存储,直接被t2线程取走了
/**
* BlockingQueue的实现类之SynchronousQueue
* 没有任何容量,必须现有线程先从队列中take,才能向queue中add数据,否则会抛出队列已满的异常。
* 不能使用peek方法取数据,此方法底层没有实现,会直接返回null
* ---------------------------------------
* 如果没有读取线程,则add方法会排除Queue Full异常,所以建议使用put方法,进行阻塞。
* 如果没有写入线程,则poll方法会无法取到数据,所以建议设置poll方法的阻塞时长,或者使用take方法进行永久阻塞
*/
public class DemoThread32 {
/**
* 测试-SynchronousQueue没有容量,第一次add就会抛出异常
*
* @author yin.hl
* @Title: test1
* @return: void
*/
public static void test1() {
SynchronousQueue<Integer> queue = new SynchronousQueue<Integer>();
queue.add(1);
}
/**
* 测试-SynchronousQueue没有容量,使用put方法阻塞,避免抛出异常
*
* @author yin.hl
* @Title: test2
* @return: void
*/
public static void test2() {
SynchronousQueue<Integer> queue = new SynchronousQueue<Integer>();
try {
queue.put(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 测试:一个线程put一个线程take,如果没有数据则take线程永远阻塞
*
* @author yin.hl
* @Title: test3
* @return: void
*/
public static void test3() {
final SynchronousQueue<Integer> queue = new SynchronousQueue<Integer>();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
// 使用get方法当队列为空时阻塞
System.out.println("take->"+queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
// 使用put方法,当队列满的时候阻塞
queue.put(1);
queue.put(2);
queue.put(3);
queue.put(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
/**
* 测试:一个线程put一个线程take, take线程最多阻塞5秒,如果还没有取到数据,则结束线程
*
* @author yin.hl
* @Title: test4
* @return: void
*/
public static void test4() {
final SynchronousQueue<Integer> queue = new SynchronousQueue<Integer>();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
//最多阻塞5秒,如果还没有取到数据,则结束线程
Integer result = queue.poll(5, TimeUnit.SECONDS);
System.out.println("poll->"+result);
if(result==null){
System.out.println("poll->5s没有数据,线程stop");
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
// 使用put方法,当队列满的时候阻塞
queue.put(1);
queue.put(2);
queue.put(3);
queue.put(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
public static void main(String[] args) {
// test1();
// test2();
// test3();
// test4();
}
}
执行test1,结果如下
Exception in thread "main" java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(AbstractQueue.java:98)
at com.mimaxueyuan.demo.middle.DemoThread32.test1(DemoThread32.java:47)
at com.mimaxueyuan.demo.middle.DemoThread32.main(DemoThread32.java:150)
执行test2,没有进程取数据,进程进入阻塞状态
执行test3,数据取完后进入阻塞状态,执行结果如下
take->1
take->2
take->3
take->4
执行test4,结果如下,一个线程put一个线程take, take线程最多阻塞5秒,如果还没有取到数据,则结束线程
poll->1
poll->2
poll->3
poll->4
poll->null
poll->5s没有数据,线程stop
PriorityBlockingQueue
PriorityBlockingQueue:一个无界阻塞队列,默认初始化长度11,也可以手动指定,但是队列会自
动扩容。资源被耗尽时导致 OutOfMemoryError。不允许使用 null元素。不允许插入不可比较的对
象(导致抛出 ClassCastException), 加入的对象实现Comparable接口
不允许添加null元素
查看源码
基于数组实现
/**
* Priority queue represented as a balanced binary heap: the two
* children of queue[n] are queue[2*n+1] and queue[2*(n+1)]. The
* priority queue is ordered by comparator, or by the elements'
* natural ordering, if comparator is null: For each node n in the
* heap and each descendant d of n, n <= d. The element with the
* lowest value is in queue[0], assuming the queue is nonempty.
*/
private transient Object[] queue;
初始容量
/**
* Default array capacity.
*/
private static final int DEFAULT_INITIAL_CAPACITY = 11;
构造函数
/**
* Creates a {@code PriorityBlockingQueue} with the default
* initial capacity (11) that orders its elements according to
* their {@linkplain Comparable natural ordering}.
*/
public PriorityBlockingQueue() {
this(DEFAULT_INITIAL_CAPACITY, null);
}
/**
* Creates a {@code PriorityBlockingQueue} with the specified
* initial capacity that orders its elements according to their
* {@linkplain Comparable natural ordering}.
*
* @param initialCapacity the initial capacity for this priority queue
* @throws IllegalArgumentException if {@code initialCapacity} is less
* than 1
*/
public PriorityBlockingQueue(int initialCapacity) {
this(initialCapacity, null);
}
/**
* Creates a {@code PriorityBlockingQueue} with the specified initial
* capacity that orders its elements according to the specified
* comparator.
*
* @param initialCapacity the initial capacity for this priority queue
* @param comparator the comparator that will be used to order this
* priority queue. If {@code null}, the {@linkplain Comparable
* natural ordering} of the elements will be used.
* @throws IllegalArgumentException if {@code initialCapacity} is less
* than 1
*/
public PriorityBlockingQueue(int initialCapacity,
Comparator<? super E> comparator) {
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.lock = new ReentrantLock();
this.notEmpty = lock.newCondition();
this.comparator = comparator;
this.queue = new Object[initialCapacity];
}
/**
* 阻塞队列之PriorityBlockingQueue(带有优先级的阻塞队列)
*
* 一个无界阻塞队列,它使用与类 PriorityQueue 相同的顺序规则,并且提供了阻塞获取操作。虽然此队列逻辑上是无界的,但是资源被耗尽时试图执行 add 操作也将失败(导致 OutOfMemoryError)。
* 此类不允许使用 null 元素。依赖自然顺序的优先级队列也不允许插入不可比较的对象(这样做会导致抛出 ClassCastException)。
* 此类及其迭代器可以实现 Collection 和 Iterator 接口的所有可选 方法。
* iterator() 方法中提供的迭代器并不 保证以特定的顺序遍历 PriorityBlockingQueue 的元素。
* 如果需要有序地进行遍历,则应考虑使用 Arrays.sort(pq.toArray())。
* 此外,可以使用方法 drainTo 按优先级顺序移除 全部或部分元素,并将它们放在另一个 collection 中。
* 在此类上进行的操作不保证具有同等优先级的元素的顺序。如果需要实施某一排序,那么可以定义自定义类或者比较器,比较器可使用修改键断开主优先级值之间的联系。
* 例如,以下是应用先进先出 (first-in-first-out) 规则断开可比较元素之间联系的一个类。
* 要使用该类,则需要插入一个新的 FIFOEntry(anEntry) 来替换普通的条目对象。
*
* 注意:加入到PriorityBlockingQueue中的元素不是立即排序的,是在调用take等读取方法之后
* ----------------------------------------------
* put、add方法实际调用了offer方法
* ----------------------------------------------
* peek 读取头元素不移除,队列为空,返回null,不阻塞, 不抛异常
* poll 读取头元素并移除,队列为空,返回null,不阻塞, 不抛异常
* poll 可指定阻塞时间,2秒,如果队列依然为空,则返回null,不抛异常
* take 读取头元素并移除,如果队列为空,则永远阻塞,不抛出异常
* drainTo 取出queue中指定个数(或全部)的元素放入list中,并移除,当队列为空时不抛出异常
*/
class Demo implements Comparable<Demo>{
private Integer id;
private String name;
public Demo(Integer id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Demo [id=" + id + ", name=" + name + "]";
}
@Override
public int compareTo(Demo o) {
if(this.id<o.id){
return -1;
}else if(this.id>o.id){
return 1;
}else{
return 0;
}
}
}
public class DemoThread33 {
/**
* 测试添加方法
* @author yin.hl
* @Title: testAdd
* @return: void
*/
public static void testAdd(){
PriorityBlockingQueue<Demo> queue = new PriorityBlockingQueue<Demo>(12);
queue.add(new Demo(3,"a")); //实际调用了offer方法
queue.offer(new Demo(1,"b"));
//queue.add(null); //不允许添加null元素
queue.put(new Demo(2,"c")); //实际调用了offer方法
queue.offer(new Demo(4,"d"), 1, TimeUnit.SECONDS); //设定阻塞时间
System.out.println("队列中的数据不是按照顺序排列的:"+queue);
}
public static void testTake1(){
PriorityBlockingQueue<Demo> queue = new PriorityBlockingQueue<Demo>(12);
queue.add(new Demo(3,"a")); //实际调用了offer方法
queue.offer(new Demo(1,"b"));
//queue.add(null); //不允许添加null元素
queue.put(new Demo(2,"c")); //实际调用了offer方法
queue.offer(new Demo(4,"d"), 1, TimeUnit.SECONDS); //设定阻塞时间
System.out.println(queue);
try {
System.out.println("take1>>"+queue.take());
System.out.println(queue); //take之后才进行排序
System.out.println("poll>>"+queue.poll());
System.out.println("take2>>"+queue.take());
System.out.println("take3>>"+queue.take());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void testTake2(){
PriorityBlockingQueue<Demo> queue = new PriorityBlockingQueue<Demo>(12);
queue.add(new Demo(3,"a")); //实际调用了offer方法
queue.offer(new Demo(1,"b"));
System.out.println("a>>"+queue);
System.out.println("b>>"+queue.peek()); // 读取头元素不移除
System.out.println("c>>"+queue);
System.out.println("d>>"+queue.poll()); // 读取头元素,并移除
System.out.println("e>>"+queue);
try {
// 获取头元素,并移除数据
System.out.println("f>>"+queue.take());
System.out.println("g>>"+queue);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("h>>"+queue.peek()); // 队列为空,返回null,不阻塞, 不抛异常
System.out.println("i>>"+queue.poll()); // 队列为空,返回null,不阻塞, 不抛异常
try {
// 可指定阻塞时间,2秒,如果队列依然为空,则返回null,不抛异常
System.out.println("j>>"+queue.poll(2, TimeUnit.SECONDS));
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
queue.take(); // 如果队列为空,则永远阻塞,不抛出异常
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("over");
}
// 测试获取数据2
public static void testTake3() {
LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(5);
queue.add(3);
queue.add(2);
queue.add(3);
queue.add(1);
queue.add(5);
ArrayList<Integer> list = new ArrayList<Integer>();
queue.drainTo(list, 4); // 取出queue中指定个数的元素放入list中,并移除
System.out.println("a>>"+list);
System.out.println("b>>"+queue);
Object[] array = list.toArray();
Arrays.sort(array);
System.out.println("sort>>"+Arrays.toString(array));
}
// 测试获取数据3
public static void testTake4() {
PriorityBlockingQueue<Integer> queue = new PriorityBlockingQueue<Integer>(3);
queue.add(3);
queue.add(2);
queue.add(1);
ArrayList<Integer> list = new ArrayList<Integer>();
queue.drainTo(list); // 取出queue中的全部元素放入list中,并移除
System.out.println("a>>"+list);
System.out.println("b>>"+queue);
Object[] array = list.toArray();
Arrays.sort(array);
System.out.println("sort>>"+Arrays.toString(array));
ArrayList<Integer> list1 = new ArrayList<Integer>();
queue.drainTo(list1); // 当队列为空时不抛出异常
System.out.println("c>>"+list1);
System.out.println("d>>"+queue);
}
public static void main(String[] args) {
// testAdd();
// testTake1();
// testTake2();
// testTake3();
testTake4();
}
}
执行testAdd,队列中的数据不是按照顺序排列的
队列中的数据不是按照顺序排列的:[Demo [id=1, name=b], Demo [id=3, name=a], Demo [id=2, name=c], Demo [id=4, name=d]]
执行testTake1,优先级队列并不是在add时候计算优先级的,在取数据的时候才会计算优先级
[Demo [id=1, name=b], Demo [id=3, name=a], Demo [id=2, name=c], Demo [id=4, name=d]]
take1>>Demo [id=1, name=b]
[Demo [id=2, name=c], Demo [id=3, name=a], Demo [id=4, name=d]]
poll>>Demo [id=2, name=c]
take2>>Demo [id=3, name=a]
take3>>Demo [id=4, name=d]
执行testTake2,take取不到数据,阻塞了
a>>[Demo [id=1, name=b], Demo [id=3, name=a]]
b>>Demo [id=1, name=b]
c>>[Demo [id=1, name=b], Demo [id=3, name=a]]
d>>Demo [id=1, name=b]
e>>[Demo [id=3, name=a]]
f>>Demo [id=3, name=a]
g>>[]
h>>null
i>>null
j>>null
执行testTake3,如何对PriorityBlockingQueue排序?先取出再Arrays.toString
a>>[3, 2, 3, 1]
b>>[5]
sort>>[1, 2, 3, 3]
执行testTake4,drainTo在队列为空时不报异常
a>>[1, 2, 3]
b>>[]
sort>>[1, 2, 3]
c>>[]
d>>[]
DelayQueue
DelayQueue:Delayed 元素的一个无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列
的头部 是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,
并且 poll 将返回 null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于等于
0 的值时,将发生到期。即使无法使用 take 或 poll 移除未到期的元素,也不会将这些元素作为正
常元素对待。例如,size 方法同时返回到期和未到期元素的计数。此队列不允许使用 null 元素。内
部元素需实现Delayed接口
场景:缓存到期删除、任务超时处理、空闲链接关闭等
基于优先级阻塞队列实现的
底层使用了PriorityQueue
public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {
private final transient ReentrantLock lock = new ReentrantLock();
private final PriorityQueue<E> q = new PriorityQueue<E>();
//省略...
}
Delayed接口,只有几个计算元素是否过期的方法
/**
* A mix-in style interface for marking objects that should be
* acted upon after a given delay.
*
* <p>An implementation of this interface must define a
* {@code compareTo} method that provides an ordering consistent with
* its {@code getDelay} method.
*
* @since 1.5
* @author Doug Lea
*/
public interface Delayed extends Comparable<Delayed> {
/**
* Returns the remaining delay associated with this object, in the
* given time unit.
*
* @param unit the time unit
* @return the remaining delay; zero or negative values indicate
* that the delay has already elapsed
*/
long getDelay(TimeUnit unit);
}
例子
/**
* 阻塞队列之DelayQueue(带有延迟功能的阻塞队列)
*
* Delayed 元素的一个无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部 是延迟期满后保存时间最长的 Delayed 元素。
* 如果延迟都还没有期满,则队列没有头部,并且 poll 将返回 null。
* 当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于等于 0 的值时,将发生到期。
* 即使无法使用 take 或 poll 移除未到期的元素,也不会将这些元素作为正常元素对待。
* 例如,size 方法同时返回到期和未到期元素的计数。此队列不允许使用 null 元素。
* 内部元素需实现Delayed接口
* ----------------------------------------------
* Demo说明:模拟一种游戏的自动退出机制,例如甲乙丙三个人分别可以免费玩游戏3、2、1秒,到时自动踢出系统
* ----------------------------------------------
* ----------------------------------------------
*/
class User implements Delayed{
private int id;
private String name;
private long endTime; //退出时间
public User(int id, String name, long endTime) {
this.id = id;
this.name = name;
this.endTime = endTime;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getEndTime() {
return endTime;
}
public void setEndTime(long endTime) {
this.endTime = endTime;
}
@Override
public String toString() {
return this.name;
}
//由于需要根据延时时间的长短,计算从数据中移除的顺序,所以需要实现compareTo方法计算优先级,类似优先级队列
@Override
public int compareTo(Delayed o) {
User user = (User)o;
if(this.endTime>user.getEndTime()){
return 1;
}else if(this.endTime<user.getEndTime()){
return -1;
}else{
return 0;
}
}
//计算剩余延迟时间;零或负值指示延迟时间已经用尽
@Override
public long getDelay(TimeUnit unit) {
return this.endTime-System.currentTimeMillis();
}
}
public class DemoThread34 {
DelayQueue<User> delayQueue = new DelayQueue<User>();
//登录游戏,加入队列
public void login(User user){
delayQueue.add(user);
System.out.println("用户("+user.getId()+")"+user.getName()+"已登录,预计下机时间为"+user.getEndTime());
}
//退出游戏,移除队列
public void logout(){
try {
System.out.println(delayQueue);
User user = delayQueue.take();
//User user = delayQueue.poll(); //不能使用poll方法,因为没有阻塞功能
System.out.println("用户("+user.getId()+")"+user.getName()+"到时自动退出,时间为"+System.currentTimeMillis()+"...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//获取当前在线人数
public int onlineSize(){
return delayQueue.size();
}
public static void main(String[] args) {
DemoThread34 demo = new DemoThread34();
//用户登录,并设置退出时间
demo.login(new User(1,"甲",30000+System.currentTimeMillis()));
demo.login(new User(2,"乙",20000+System.currentTimeMillis()));
demo.login(new User(3,"丙",10000+System.currentTimeMillis()));
while(true){
//监控到时用户
demo.logout();
//如果在线用户则退出
if(demo.onlineSize()==0){
break;
}
}
}
}
执行结果
用户(1)甲已登录,预计下机时间为1614327308932
用户(2)乙已登录,预计下机时间为1614327298932
用户(3)丙已登录,预计下机时间为1614327288932
[丙, 甲, 乙]
用户(3)丙到时自动退出,时间为1614327288932...
[乙, 甲]
用户(2)乙到时自动退出,时间为1614327298932...
[甲]
用户(1)甲到时自动退出,时间为1614327308932...