队列分为两类
阻塞队列
BlockingQueue提供如下两个支持阻塞的方法:
(1)put(E e): 尝试把e元素放如BlockingQueue中,如果该队列的元素已满,则阻塞该线程。
(2)take(): 尝试从BlockingQueue的头部取出元素,如果该队列的元素已空,则阻塞该线程。
jdk实现的有以下几种:
ArrayBlockingQueue,底层是数组,有界队列
LinkedBlockingQueue,,底层是链表,无界队列
PriorityBlockingQueue,一个带优先级的队列,上put时是不会受阻的,但是如果队列为空,取元素的操作take就会阻塞。另外,往入该队列中的元 素要具有比较能力。
PriorityBlockingQueue里面存储的对象必须是实现Comparable接口。队列通过这个接口的compare方法确定对象的priority。 【下面会有代码举例说明】
SynchronousQueue 是一个没有数据缓冲的阻塞队列,(只能存储一个元素)准确的说 他不存储元素,放入元素必须等待取走以后,才能放入新的元素
DelayQueue, 只有在队列中的元素到期后才能取出。里面存储的对象必须实现Delayed接口,里面有两个方法【下面会有代码举例说明】
非阻塞队列
ConcurrentLinkedQueue 性能最好,是一个无界队列
add 和 offer都是加入元素方法(没有差别)
poll和peek都是获取头元素的方法,区别在于前者会删除元素,后者不会删除
下面举例说明PriorityBlockingQueue 和 DelayQueue的用法
/** * PriorityBlockingQueue测试 * * */ public class TestPriorityQueue2 { public static void main(String[] args) throws InterruptedException { PriorityBlockingQueue<Student> priorityBlockingQueue=new PriorityBlockingQueue<>(); priorityBlockingQueue.put(new Student("stu1",2)); priorityBlockingQueue.put(new Student("stu2",3)); priorityBlockingQueue.put(new Student("stu3",2)); priorityBlockingQueue.put(new Student("stu4",4)); System.out.println(priorityBlockingQueue.take().toString()); } } class Student implements Comparable<Student> { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Student(String name, Integer age) { this.name = name; this.age = age; } // //正序 // @Override // public int compareTo(Student o) { // return this.age>o.age?1:this.age<o.age?-1:0; // } //逆序 @Override public int compareTo(Student o) { return this.age<o.age?1:-1; } @Override public String toString() { return "Student{" + "name=" + name + ", age=" + age + '}'; } }
接下来是DelayQueue,主要应用场景有以下几个
1) 关闭空闲连接。服务器中,有很多客户端的连接,空闲一段时间之后需要关闭之。
2) 缓存。缓存中的对象,超过了空闲时间,需要从缓存中移出。
3) 任务超时处理。在网络协议滑动窗口请求应答式交互时,处理超时未响应的请求。
下面举一个例子
public class Wangming implements Delayed { private String name; // 截止时间 private long endTime; public Wangming(String name, String id, long endTime) { this.name = name; this.endTime = endTime; } public Wangming(String name, long endTime) { this.name = name; this.endTime = endTime; } public String getName() { return this.name; } /** * 用来判断是否到了截止时间 */ @Override public long getDelay(TimeUnit unit) { return endTime - System.currentTimeMillis(); } /** * 相互比较排序用,延迟队列队列头总是存储快要过期的数据 */ @Override public int compareTo(Delayed o) { Wangming wangming = (Wangming) o; return endTime - wangming.endTime > 0 ? 1 : -1; } @Override public String toString() { return "Wangming [name=" + name + ", endTime=" + endTime + "]"; } public static void main(String[] args) { DelayQueue<Wangming> queue = new DelayQueue<Wangming>(); Wangming man = new Wangming("a", 1000 * 11 + System.currentTimeMillis()); Wangming man1 = new Wangming("b", 1000 * 3 + System.currentTimeMillis()); Wangming man2 = new Wangming("c", 1000 * 2 + System.currentTimeMillis()); queue.add(man); queue.add(man1); queue.add(man2); try { while (true) { System.out.println(queue.take()); } } catch (InterruptedException e) { e.printStackTrace(); } } }
下面简单说明下DelayQueue的原理
DelayQueue内部的实现使用了一个优先队列。当调用DelayQueue的offer方法时,把Delayed对象加入到优先队列q中。如下:
public boolean offer(E e) { if (e == null) throw new NullPointerException(); modCount++; int i = size; if (i >= queue.length) //长度不够扩容 grow(i + 1); size = i + 1; if (i == 0) //还没有元素 会直接加入 queue[0] = e; else //有元素了,排序加入 加入顺序跟你的比较器有关 siftUp(i, e); return true; }
关于take()方法
public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { for (;;) { E first = q.peek(); if (first == null) available.await(); else { long delay = first.getDelay(NANOSECONDS); if (delay <= 0) return q.poll(); first = null; // 防止内存泄漏 if (leader != null) available.await(); else { Thread thisThread = Thread.currentThread(); leader = thisThread; try { available.awaitNanos(delay); } finally { if (leader == thisThread) leader = null; } } } } } finally { if (leader == null && q.peek() != null) available.signal(); lock.unlock(); } }
基本流程比较简单 ,主要这里需要注意2点
first = null //主要是为了防止内存泄漏
leader 减少等待时间