一.缺陷
1.非核心线程的创建时机
1.1) 核心线程的数量是 corePoolSize 的值,非核心线程的数量是 maxinumPoolSize - corePoolSize ;
1.2) 非核心线程创建的触发时机是:当前线程池中核心线程已满,且没有空闲的线程,还有任务等待队列已满,
满足上面的所有条件,才会去创建线程去执行新提交的任务;
1.3) 如果线程池中的线程数量达到 maxinumPoolSize 的值,此时还有任务进来,就会执行拒绝策略,抛弃任务或者其他
如果拒绝策略是抛弃任务的话,有一种场景,就会造成大量任务的丢弃,就是瞬时冲高的情况下。
2.排队任务调度策略
当线程池中核心线程数量已达标,且没有空闲线的情况下,在产生的任务,会加入到等待队列中去,这样一直持续下去,
等到等待队列已满,在来的任务,会创建非核心线程去执行新提交的任务,那么就产生一种结果,在等待队列中的任务是先提
交的任务,反而没有在此时提交的任务先执行。
任务的执行顺序和任务的提交顺序不一致,如果业务需求的任务是有先后依赖关系的,就会降低线程的调度效率
二.优化策略
1. 非核心线程的创建时机
Java线程池的调度策略是,当等待队列已满,才会去创建分核心线程去执行新的任务
优化方案:是自定义一个等待队列,当队列的值达到一定的阈值,就开始创建分核心线程去执行,
而不是等到队列已满,才去创建
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 当插入队列workQueue.offer(command)失败时,才会去创建非核心线程
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
1.1) 自定义等待队列,采用LinkedBlockingQueue作为基本队列,产生一个双生产者,单消费者的队列,将
生产者分为普通生产者,和超限生产者
- 普通生产者:使用BlockingQueue原生的offer等接口添加任务,可用队列大小为 queueSize*threshold,Java
原生线程池作为生产者调用该组接口
- 超限生产者:使用自定义的additionOffer接口添加任务,可用队列大小为 queueSize*(1-threshold),我们复写
的线程池框架负责执行次动作
package com.roger.threadpool.queue;
import java.util.Collection;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* 定制双生产者,单消费者队列
* 通过阈值叫队列分为普通生产队列和超限生产队列
* 普通生产队列通过Java原生的线程池使用
* 超限生产队列通过自定义的线程池使用
* @param <T>
*/
public class CustomThreadBlockingQueue<T> extends LinkedBlockingQueue<T> {
private int capacity;
private int additionalCapacity;
// 处理原则:capacity,additionalCapacity生产的时候分开,消费的时候一起消费
public CustomThreadBlockingQueue(int queueSize,double threshold) {
super(queueSize);
this.capacity = new Double(queueSize * threshold).intValue();
this.additionalCapacity = queueSize - this.capacity;
}
@Override
public boolean addAll(Collection<? extends T> c){
throw new UnsupportedOperationException();
}
//普通生产者接口
@Override
public void put(T e) throws InterruptedException {
throw new UnsupportedOperationException();
}
//普通生产者接口
@Override
public boolean add(T e) {
if (super.size() >= capacity) {
throw new IllegalStateException();
}
//并发下不足够精确
return super.add(e);
}
//普通生产者
@Override
public boolean offer(T t) {
//并发下不足够精确
if (super.size() >= capacity) {
return false;
}
return super.offer(t);
}
@Override
public boolean offer(T t, long timeout, TimeUnit unit) throws InterruptedException {
//并发下不足够精确
if (super.size() >= capacity) {
return false;
}
return super.offer(t, timeout, unit);
}
//符合LVS替换原则,弱化为BlockingQueue时为标准的单生产者、消费者队列
@Override
public int remainingCapacity() {
int remain = super.remainingCapacity() - additionalCapacity;
return remain >= 0 ? remain : 0;
}
//超限生产者接口
public boolean additionalOffer(T e) {
return super.offer(e);
}
}
1.2) 上面的队列中,普通生产者是通过java原生的线程池去使用,但是超限生产者,需要自定义的线程池去使用
package com.roger.threadpool.queue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CustomThreadPoolExecutor extends ThreadPoolExecutor {
public CustomThreadPoolExecutor(int poolSize, int maxSize, int keepAliveTime, TimeUnit timeUnit, int queueSize, double threshold, RejectedExecutionHandler handler) {
super(poolSize, maxSize, keepAliveTime, timeUnit,
new CustomThreadBlockingQueue<Runnable>(queueSize, threshold));
super.setRejectedExecutionHandler(new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
CustomThreadBlockingQueue<Runnable> blockingQueue = (CustomThreadBlockingQueue<Runnable>) executor.getQueue();
// 如果超限任务添加成功,则创建非核心线程去执行任务
if(!blockingQueue.additionalOffer(r)){
//如果超限任务添加失败,转由外部处理
handler.rejectedExecution(r,executor);
}
}
});
}
}
2.任务调度策略 - 希望任务调度是有序的
基本思路:
2.1) 采用任务代理类,将任务绑定时机延迟到任务执行时,而不是添加时
2.2) 增加新的任务队列,按添加顺序保存真正的执行任务
2.3) 运行时,动态从新增任务队列中获取头部任务,做到FIFO
package com.roger.threadpool;
import java.util.concurrent.BlockingQueue;
public class RunTaskProxy implements Runnable {
private BlockingQueue<Runnable> realTaskQueue = null;
public RunTaskProxy(BlockingQueue<Runnable> realTaskQueue) {
this.realTaskQueue = realTaskQueue;
}
@Override
public void run() {
//运行时绑定,头部获取任务,保证FIFO
Runnable runnable = realTaskQueue.remove();
runnable.run();
}
}
package com.roger.threadpool.queue;
import java.util.concurrent.*;
public class CustomThreadPoolExecutor extends ThreadPoolExecutor {
//真正任务保序队列
private BlockingQueue<Runnable> realTaskQueue = new LinkedBlockingQueue<>();
public CustomThreadPoolExecutor(int poolSize, int maxSize, int keepAliveTime, TimeUnit timeUnit, int queueSize, double threshold, RejectedExecutionHandler handler) {
super(poolSize, maxSize, keepAliveTime, timeUnit,
new CustomThreadBlockingQueue<Runnable>(queueSize, threshold));
super.setRejectedExecutionHandler(new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
CustomThreadBlockingQueue<Runnable> blockingQueue = (CustomThreadBlockingQueue<Runnable>) executor.getQueue();
// 如果超限任务添加成功,则创建非核心线程去执行任务
if(!blockingQueue.additionalOffer(r)){
//移除任务,任务真正拒绝
realTaskQueue.remove(r);
//如果超限任务添加失败,转由外部处理
RejectedExecutionHandler handler = executor.getRejectedExecutionHandler();
handler.rejectedExecution(r,executor);
}
}
});
}
@Override
public void execute(Runnable command) {
// 任务记录
realTaskQueue.add(command);
// 封装代理对象
super.execute(new RunTaskProxy(realTaskQueue));
}
}