善治病者,必医其受病之处;
善救弊者,必塞其弊之源
书接上文线程池的设计思路,带着如何设计拒绝策略的你,又回顾了整个流程,突然你想这事因排队而起,能不能在排队的队列上有所改进。
第一部分 排队也是值得考虑的
BlockingQueue:任务队列
我们来看一下Doug Lea事先给我们设计好的队列:
1. LinkedBlockingQueue
基于链表实现的(无界的)阻塞队列,默认情况下容量是 Integer.MAX_VALUE,可以说是无界的。
只有调用这个构造函数才可以设置容量capacity,否则都是Integer.MAX_VALUE。
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
2. ArrayBlockingQueue
基于数组实现的有界阻塞队列。默认情况下,不保证FIFO的顺序(By default, this ordering is not guaranteed).。因为公平策略将会降低吞吐率,但是也降低了可变性以及避免了饥饿现象.(Fairness generally decreases throughput but reduces variability and avoids starvation.)
// 注意: 参数fair默认为false, true -保证FIFO
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
3. SynchronousQueue
A blocking queue in which each insert operation must wait for a corresponding remove operation by another thread, and vice versa(反之亦然).
同步队列-不存储任务的阻塞队列。插入操作 和 删除操作是一一对应的。就像一个空集合(acts as an empty collection)
三者之间的联系
- 吞吐率角度:
SynchronousQueue > LinkedBlockingQueue > ArrayBlockingQueue
- FIFO的角度:
LinkedBlockingQueue 直接按照队列的FIFO设计;
SynchronousQueue, ArrayBlockingQueue 默认都是不保证FIFO顺序的,可以在构造函数中打开fairness。
- 容量角度:
LinkedBlockingQueue默认容量是无限制的,直接打翻了“临时工”的饭碗。即:maxiumPoolSize失去了意义。PS:2333, "正式工"已经哭晕倒厕所里啦,往死里干。
世界本来就是不公平的,面对现实吧!!!
4. PriorityBlockingQueue
具有优先级的阻塞队列,初始容量是11。Obviously, 它不保证FIFO,如果需要可以自定义类去打破其优先级(有病啊?OR闲得啦?).
还有几种阻塞队列,但是我认为记住这四种差不多就够用啦,后面的遇到再介绍。怕:消化不良 2333
BlockingQueue归纳一下:
尽情地发挥想象:
- LinkedBlockingQueue:为了防止插队,用绳索把每个人都串联起来(PS:这绳索默认还是无限长地(Integer.MAX_VALUE)),MMP,还得请城管大队长喝酒啊;
- ArrayBlockingQueue:绑起来貌似有点不厚道,还是把绳索解开,但是都别乱动啊。为了不惊动城管,不准站太多人。
- PriorityBlockingQueue:城管大人站在队列地尾部,对你投来迷之微笑,吓得你毛骨悚然,怕遇到“同志”。正在发愣地时候,城管大人一只手搭在你的肩膀上,老弟不请我吃一顿。此时你那还管什么FIFO啊,直接拉着城管大人地手往店里走。
- SynchronousQueue:城管大人说饱喝足,走出门口说:上面检查,排队影响市容市貌,不准排队。你看着办吧。只能(来一个走一个,走一个来一个)。PS:一首凉凉送给你
第二部分:学会拒绝
RejectedExecutionHandler 拒绝策略
1. AbortPolicy
简单粗暴,你值得选择。
默认策略:直接拒绝并抛出运行时异常:RejectedExecutionException。
2. DiscardPolicy
冲动是魔鬼,默默地拒绝。
直接拒绝但不抛异常。
3. DiscardOldestPolicy
为了大局而牺牲局部利益
直接拒绝队列的头部,为了不犯众怒,你只好对队列的第一个人说不。毕竟:得罪一个,取悦一群啊。
4. CallerRunsPolicy
从哪来回哪去
交给调用者所在的线程来处理任务。
5. 自定义策略
随心所欲
实现RejectedExecutionHandler接口。比如送个优惠券啥的…
RejectedExecutionExecutor总结
这里假设:正是饥饿的时候,不让吃饭谁能心里舒服,谁也不愿意啊,
- AboryPolicy :直接拒绝结果把人给打了一顿,这样饭店也干不下去啦…
- DiscardPolicy:陪着笑脸,打不还手骂不还口直的拒绝
- DiscardOldestPolicy: 为了大部分人的利益而牺牲少数人。排队的人在后面起哄,质问前面的人怎么还没走啊。为了平复后面的人,只能牺牲一下队列首位啦。PS:电车悖论
- CallerRunsPolicy : 冷处理
- 自定义策略:为了不得罪顾客,可能你要发个优惠券啥的。
通过这两篇文章,我认为大家对ThreadPoolExecutor应该有了一个清晰的认识。更为详细的内容,后续慢慢补充。接下来要实现你做大老板的梦想——开连锁店。