博客概述
高性能=非阻塞。阻塞=非高性能。队列queue:先进先出特性。
具体实现
ConcurrentLinkedQueue
queue接口里面有add和offer方法,ConcurrentLinkedQueue中没有区别。但是其他的子类实现可能会有区别。
ConcurrentLinkedQueue<String> q = new ConcurrentLinkedQueue<String>();
q.offer("a");
q.offer("b");
q.offer("c");
q.offer("d");
q.add("e");
System.out.println(q.poll()); //a 从头部取出元素,并从队列里删除
System.out.println(q.size()); //4 此时是队列长度是4
System.out.println(q.peek()); //b 从头部取出元素,但是不删除
System.out.println(q.size()); //4 此时长度还是4
BlockingQueue
阻塞队列,底层有两种实现,分别是链表和数组。对于有界阻塞队列,内部没有实现读写分离,生产与消费不能完全并行。对于无界阻塞队列,内部实现分离锁,实现读写分离,从而实现生产消费完全并行。
//有界阻塞队列
final ArrayBlockingQueue<String> array = new ArrayBlockingQueue<String>(6);
array.put("a");
array.put("b");
array.add("c");
array.add("d");
array.add("e");
array.add("f");//使用add方法放不进去会报错java.lang.IllegalStateException: Queue full
new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
//array.poll();
}
}).start();
//如果能规定时间之内放进去返回true,否则返回false,offer就不会报错
System.out.println(array.offer("h", 5, TimeUnit.SECONDS));
无界阻塞队列:可以加参数,限制长度。不加参数就是无限长度。
offer添加,当添加第六个的时候,不会报错。也就是说是一种尝试性添加。
但是add添加,添加第6个的时候,会报错。强制添加,不行就是不行。
//阻塞队列
LinkedBlockingQueue<String> q = new LinkedBlockingQueue<String>();
q.offer("a");
q.offer("b");
q.offer("c");
q.offer("d");
q.offer("e");
q.add("f");
System.out.println(q.size());
for (Iterator iterator = q.iterator(); iterator.hasNext();) {
String string = (String) iterator.next();
System.out.println(string);
}
//操作:批量的从集合中取元素放到容器中。
List<String> list = new ArrayList<String>();
System.out.println(q.drainTo(list, 3));//拿出来前三个
System.out.println(list.size()); //3
for (String string : list) {
System.out.println(string); // a b c
}
synchronousQueue
这是一个无缓存的queue,内部没有存储元素的容器。加元素,报异常。但是不是add方法不能用。有特殊应用场景。是加了数据马上拿走。
final SynchronousQueue<String> q = new SynchronousQueue<String>();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(q.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
q.add("asdasd");
}
});
t2.start();
案例介绍
有一个web程序,有多个client端,比如公路系统。早8点是早高峰,车理解为任务,公路理解为web程序。车多就得限制,限流。有界队列,无界队列,不能加元素的队列,在巅峰期得用有界队列,无界系统就撑爆了,比如有一个1000任务长度的队列做缓冲,超出1000的就得等着没有响应(拒绝策略,系统正忙这种提示)。一段时间并发很大,选择有界的阻塞队列。不能无限制的加元素。
如果超过上限元素,可以执行拒绝策略。
平稳期可以使用linked队列,来的车不多,并发量不大,容量允许。
使用的低谷期,通过虚拟队列sync,,处理能力不够,但是车不够多,比如凌晨2点,没有任何容量,直接给线程去处理掉。sync队列。提高效率。
你的web应用根据实际场景切换容器。
优先级阻塞队列与延迟队列
优先级队列
优先级队列的底层不是加一个元素排一次顺序(这样省性能)。而是调用take方法时,拿出优先级最高的元素。拿出一个之后,就自动按照优先级进行选择排序。把优先级最高的放在最前面(选择排序)。加入元素之后直接迭代展示,并没有排序效果。
public class Task implements Comparable<Task>{
private int id ;
private String name;
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;
}
@Override
public int compareTo(Task task) {
return this.id > task.id ? 1 : (this.id < task.id ? -1 : 0);
}
public String toString(){
return this.id + "," + this.name;
}
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.PriorityBlockingQueue;
public class UsePriorityBlockingQueue {
public static void main(String[] args) throws Exception{
PriorityBlockingQueue<Task> q = new PriorityBlockingQueue<Task>();
Task t1 = new Task();
t1.setId(3);
t1.setName("id为3");
Task t2 = new Task();
t2.setId(4);
t2.setName("id为4");
Task t3 = new Task();
t3.setId(1);
t3.setName("id为1");
//return this.id > task.id ? 1 : 0;
q.add(t1); //3
q.add(t2); //4
q.add(t3); //1
// 1 3 4
System.out.println("容器:" + q);
System.out.println(q.take().getId());
System.out.println("容器:" + q);
// System.out.println(q.take().getId());
// System.out.println(q.take().getId());
}
}
延迟队列
使用一个网吧营业的demo来展示。
import java.util.concurrent.DelayQueue;
public class WangBa implements Runnable {
private DelayQueue<Wangmin> queue = new DelayQueue<Wangmin>();
public boolean yinye =true;
public void shangji(String name,String id,int money){
Wangmin man = new Wangmin(name, id, 1000 * money + System.currentTimeMillis());
System.out.println("网名"+man.getName()+" 身份证"+man.getId()+"交钱"+money+"块,开始上机...");
this.queue.add(man);
}
public void xiaji(Wangmin man){
System.out.println("网名"+man.getName()+" 身份证"+man.getId()+"时间到下机...");
}
@Override
public void run() {
while(yinye){
try {
Wangmin man = queue.take();
xiaji(man);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String args[]){
try{
System.out.println("网吧开始营业");
WangBa siyu = new WangBa();
Thread shangwang = new Thread(siyu);
shangwang.start();
siyu.shangji("路人甲", "123", 1);
siyu.shangji("路人乙", "234", 10);
siyu.shangji("路人丙", "345", 5);
}
catch(Exception e){
e.printStackTrace();
}
}
}
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class Wangmin implements Delayed {
private String name;
//身份证
private String id;
//截止时间
private long endTime;
//定义时间工具类
private TimeUnit timeUnit = TimeUnit.SECONDS;
public Wangmin(String name,String id,long endTime){
this.name=name;
this.id=id;
this.endTime = endTime;
}
public String getName(){
return this.name;
}
public String getId(){
return this.id;
}
/**
* 用来判断是否到了截止时间
*/
@Override
public long getDelay(TimeUnit unit) {
//return unit.convert(endTime, TimeUnit.MILLISECONDS) - unit.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
return endTime - System.currentTimeMillis();
}
/**
* 相互批较排序用
*/
@Override
public int compareTo(Delayed delayed) {
Wangmin w = (Wangmin)delayed;
return this.getDelay(this.timeUnit) - w.getDelay(this.timeUnit) > 0 ? 1:0;
}
dequeue
Deque是Queue的子接口,我们知道Queue是一种队列形式,而Deque则是双向队列,它支持从两个端点方向检索和插入元素,因此Deque既可以支持LIFO形式也可以支持LIFO形式.Deque接口是一种比Stack和Vector更为丰富的抽象数据形式,因为它同时实现了以上两者.
public static void main(String[] args) {
LinkedBlockingDeque<String> dq = new LinkedBlockingDeque<String>(10);
dq.addFirst("a");
dq.addFirst("b");
dq.addFirst("c");
dq.addFirst("d");
dq.addFirst("e");
dq.addLast("f");
dq.addLast("g");
dq.addLast("h");
dq.addLast("i");
dq.addLast("j");
//dq.offerFirst("k");
System.out.println("查看头元素:" + dq.peekFirst());
System.out.println("获取尾元素:" + dq.pollLast());
Object [] objs = dq.toArray();
for (int i = 0; i < objs.length; i++) {
System.out.println(objs[i]);
}
}
```