什么是线程池
线程池是一个存放线程并利用存放的线程来执行业务线程的容器,降低了频繁创建和销毁线程的性能开销和时间开销。
线程池的使用
ExecutorService service=Executors.newFixedThreadPool(3);//1
ExecutorService service=Executors.newCachedThreadPool();//2
ExecutorService service=Executors.newSingleThreadExecutor();//3
ExecutorService service=Executors.newScheduledThreadPool(3);//4
- 返回一个固定数量的线程池,线程数不变,当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会被暂缓在一个任务队列中,等待有空闲的线程去执行。
- 一个可根据实际情况调整线程个数的线程池,不限制最大线程数量,若用空闲的线程则执行任务,若无任务则不创建线程。并且每一个空闲线程会在 60 秒后自动回收。
- 创建一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列中。
- 创建一个可以指定线程的数量的线程池,但是这个线程池还带有延迟和周期性执行任务的功能,类似定时器。
而这4中创建线程池的方式,都是基于ThreadpoolExecutor实现的
public ThreadPoolExecutor(int corePoolSize, //核心线程数量
int maximumPoolSize, //最大线程数
long keepAliveTime, //超时时间,超出核心线程数量以外的线程空余存活时间
TimeUnit unit, //存活时间单位
BlockingQueue<Runnable> workQueue, //保存执行任务的队列
ThreadFactory threadFactory,//创建新线程使用的工厂
RejectedExecutionHandler handler //当任务无法执行的时候的处理方式)
执行过程
线程池在创建之初是没有初始化线程的,在第一次执行业务线程时才会创建,任务完成之后,将不超过核心线程数的线程挂起供下一次执行任务时使用。
在执行任务时,当线程数大于等于核心线程数时,会将新进入的线程放入队列,如果队列也满了,那就创建新的线程去执行任务,直到线程数打到所设置的最大线程数时,执行拒绝策略,或者实现 RejectedExecutionHandler 接口自定义饱和策略。
源码浅谈
execute
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {// 1
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {// 2
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))//3
reject(command);
else if (workerCountOf(recheck) == 0)//4
addWorker(null, false);
}
else if (!addWorker(command, false))//5
reject(command);
}
- 如果工作线程数小于核心线程数,则新建线程执行任务。
- 否则将任务加入队列。
- 如果线程池状态为非运行,则将任务从队列中删除,并拒绝任务。
- 如果线程数量为0,则新建线程。
- 若工作线程数已达到核心线程数,并且队列也已经满了,则继续添加新的线程来执行任务,若失败则拒绝任务。
addWorker
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) { // 1
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try { // 2
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
- 这段双重自旋做了以下操作,如果线程状态是shutdown,则不添加新任务;如果线程数达到了核心线程数,则返回上层添加至队列;上树条件都没达到,则将线程数原子加一。
- 构建一个worker线程。加重入锁将将线程加入workers集合,若添加成功启动线程执行任务。
拒绝策略
- AbortPolicy:直接抛出异常,默认策略;
- CallerRunsPolicy:用调用者所在的线程来执行任务;
- DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
- DiscardPolicy:直接丢弃任务;
- 实现 RejectedExecutionHandler 接口,自定义饱和策略
注意事项
shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务
shutdown()并不会等待线程全部执行完毕,若想等待线程执行完毕,可以使用线程计数器CountDownLatch
线程池的监控
在实际业务中,可以继承ThreadPoolExecutor重写beforeExecute和afterExecute方法来自定义任务执行前后的逻辑,或记录日志来进行统计分析。
public class DemoThreadPoolExecutor extends ThreadPoolExecutor {
// 保存任务开始执行的时间 , 当任务结束时 , 用任务结束时间减去开始时间计算任务执行时间
private ConcurrentHashMap<String, Date> startTimes;
public DemoThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long
keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit,
workQueue);
this.startTimes=new ConcurrentHashMap<>();
}
@Override
public void shutdown() {
System.out.println(" 已经执行的任务数: "+this.getCompletedTaskCount()+"," +
" 当前活动线程数:"+this.getActiveCount()+", 当前排队线程数:"+this.getQueue().size());
System.out.println();
super.shutdown();
}
// 任务开始之前记录任务开始时间
@Override
protected void beforeExecute(Thread t, Runnable r) {
startTimes.put(String.valueOf(r.hashCode()),new Date());
super.beforeExecute(t, r);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
Date startDate = startTimes.remove(String.valueOf(r.hashCode()));
Date finishDate = new Date();
long diff = finishDate.getTime() - startDate.getTime();
// 统计任务耗时、初始线程数、核心线程数、正在执行的任务数量、
// 已完成任务数量、任务总数、队列里缓存的任务数量、
// 池中存在的最大线程数、最大允许的线程数、线程空闲时间、线程池是否关闭、线程池是否终止
System.out.print(" 任务耗时:"+diff+"\n");
System.out.print(" 初始线程数:"+this.getPoolSize()+"\n");
System.out.print(" 核心线程数:"+this.getCorePoolSize()+"\n");
System.out.print(" 正在执行的任务数量:"+this.getActiveCount()+"\n");
System.out.print(" 已经执行的任务数:"+this.getCompletedTaskCount()+"\n");
System.out.print(" 任务总数:"+this.getTaskCount()+"\n");
System.out.print(" 最大允许的线程数:"+this.getMaximumPoolSize()+"\n");
System.out.print(" 线程空闲时间:"+this.getKeepAliveTime(TimeUnit.MILLISECONDS)+"\n");
System.out.println();
super.afterExecute(r, t);
}
public static ExecutorService newCachedThreadPool() {
return new DemoThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new
SynchronousQueue ());
}
}
public class test1 implements Runnable {
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
static ExecutorService executorService = DemoThreadPoolExecutor.newCachedThreadPool();
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
executorService.execute(new test1());
}
executorService.shutdown();
}
}