ThreadPoolExecutor
ThreadPoolExecutor类中的核心构造器
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
构造方法参数:
- corePoolSize:核心池大小。默认情况下,创建一个线程池初始线程个数为0,当有任务的时候才会去创建线程,当创建的线程数达到corePoolSize后,就会把新到达的任务放到阻塞队列中。除了调了prestartAllCoreThreads()或者prestartCoreThread()方法,这两个方法会在初始化的时候就创建所有核心线程或1个核心线程。
- maximumPoolSize:最大线程数。表示线程池最多能创建的线程数量。
- keepAliveTime:线程空闲时间。默认情况下只有当线程数超过了corePoolSize这个参数才会起作用,一个线程的空闲时间如果超过了keepAliveTime,就会被销毁,直到线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean),keepAliveTime参数会一直生效,直到线程池数量为0.
- unit:keepAliveTime的时间单位。在TimeUnit类中有7种静态属性。
- workQueue:阻塞队列。用于存储等待执行的任务。一般有
ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;
* threadFactory:线程工厂。用于创建线程。
* handler:线程池拒绝处理任务时策略。一般有:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
线程池实现原理
线程池状态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
这里有个很关键的字段ctl,因为这个变量控制了线程状态和线程数量,且具有原子性。很神奇,看看源码注释是怎么说的:
/**
* The main pool control state, ctl, is an atomic integer packing
* two conceptual fields
* workerCount, indicating the effective number of threads
* runState, indicating whether running, shutting down etc
*
* In order to pack them into one int, we limit workerCount to
* (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2
* billion) otherwise representable. If this is ever an issue in
* the future, the variable can be changed to be an AtomicLong,
* and the shift/mask constants below adjusted. But until the need
* arises, this code is a bit faster and simpler using an int.
*/
由于一个int类型是由32位的二进制数构成的,这里很巧妙的利用高3位表示runState,用低29位表示workerCount。这段代码让我感觉这个类非常的精细。解释称2^29 - 1 大概5亿。如果之后不够的话可以将int类型改成long,但是现在使用int会比较简单有效。
以下将介绍几个ThreadPoolExecutor类中常见且相关的几个方法:
private static int ctlOf(int rs, int wc) { return rs | wc; }
其中rs就是高三位的runState,wc就是低29位的workerCount。
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
c就是前面ctl的值。这两个方法分别用来获取runState和workerCount。
其中CAPACITY的定义:
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
可以看出CAPACITY其实就是workerCount的最大值。
/**
* The runState provides the main lifecyle control, taking on values:
*
* RUNNING: Accept new tasks and process queued tasks
* SHUTDOWN: Don't accept new tasks, but process queued tasks
* STOP: Don't accept new tasks, don't process queued tasks,
* and interrupt in-progress tasks
* TIDYING: All tasks have terminated, workerCount is zero,
* the thread transitioning to state TIDYING
* will run the terminated() hook method
* TERMINATED: terminated() has completed
*/
- RUNNING:可以接收新任务和处理阻塞列表中的任务
- SHUTDOWN:不能接收新任务,但是会继续处理阻塞列表中的任务
- STOP:不能接收新任务且也不会再处理阻塞列表中的任务
- TIDYING:所有任务都结束了,workCount为0,并且会调用terminated()
- TERMINATED:terminated()调用结束
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
代码中会有rs < SHUTDOWN 的判断,其实就是指RUNNING
示例
/**
* Created by cxx on 2017/6/16.
*/
public class TreadPoolTest {
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(5),new ThreadPoolExecutor.DiscardPolicy());
System.out.println("启动后线程数量:"+pool.getPoolSize());
for(int i=0;i<20;i++){
for (;;){
if (pool.getQueue().size() <5 || pool.getPoolSize() < 10){
Task myTask = new Task(i);
pool.execute(myTask);
System.out.println("线程池数:"+pool.getPoolSize()+",队列数:"+
pool.getQueue().size());
break;
}
}
}
pool.shutdown();
}
}
class Task implements Runnable {
private int taskNum;
public Task(int num) {
this.taskNum = num;
}
@Override
public void run() {
System.out.println("开始执行 "+taskNum);
try {
Thread.currentThread().sleep(3000);
} catch (InterruptedException e) {
System.out.println("被中断啦。。。。。。。。。。");
}
System.out.println("task "+taskNum+"执行完毕");
}
}
运行结果
启动后线程数量:0
线程池数:1,队列数:0
开始执行 0
线程池数:2,队列数:0
开始执行 1
线程池数:3,队列数:0
开始执行 2
线程池数:4,队列数:0
开始执行 3
线程池数:5,队列数:0
开始执行 4
线程池数:5,队列数:1
线程池数:5,队列数:2
线程池数:5,队列数:3
线程池数:5,队列数:4
线程池数:5,队列数:5
线程池数:6,队列数:5
开始执行 10
线程池数:7,队列数:5
开始执行 11
线程池数:8,队列数:5
开始执行 12
线程池数:9,队列数:5
开始执行 13
线程池数:10,队列数:5
开始执行 14
task 0执行完毕
task 4执行完毕
开始执行 5
task 3执行完毕
task 1执行完毕
开始执行 8
task 2执行完毕
开始执行 9
task 12执行完毕
开始执行 7
task 11执行完毕
task 10执行完毕
开始执行 6
task 14执行完毕
task 13执行完毕
线程池数:10,队列数:1
开始执行 15
开始执行 16
线程池数:10,队列数:1
线程池数:10,队列数:1
开始执行 17
开始执行 18
线程池数:10,队列数:1
线程池数:10,队列数:1
开始执行 19
task 8执行完毕
task 9执行完毕
task 5执行完毕
task 7执行完毕
task 6执行完毕
task 15执行完毕
task 16执行完毕
task 17执行完毕
task 19执行完毕
task 18执行完毕
Process finished with exit code 0
看运行结果可以看到,当核心数达到corePoolSize,就会往workQueue添加,workQueue满了之后,就会创建新的线程直到线程数达到maximumPoolSize。
在嵌套循环中判断线程和队列是否饱和,如果饱和就等会在添加。(现在添加的话会被丢弃,因为策略配置的是丢弃并且不抛异常)
合理配置线程池大小
在javadoc中并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态类:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
固定线程数线程池,核心和最大线程数都一样。由于keepAliveTime为0,所以一旦线程启动后除非被shutdown,否则会一直等待新任务。队列是一个没有大小限制的队列,你唯一要担心的是内存够不够用。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
相当于固定线程数线程池的线程数为1.但是我还注意到这里返回的是FinalizableDelegatedExecutorService类。好奇心让我点开了这个类:
static class FinalizableDelegatedExecutorService
extends DelegatedExecutorService {
FinalizableDelegatedExecutorService(ExecutorService executor) {
super(executor);
}
protected void finalize() {
super.shutdown();
}
}
是一个Executors的内部类,构造函数调用父类的构造方法,覆盖了finalize方法,用于垃圾回收时触发shutdown。(这一点暂时不去深究,为什么要去这么做)。在点开父类:
static class DelegatedExecutorService extends AbstractExecutorService {
private final ExecutorService e;
DelegatedExecutorService(ExecutorService executor) { e = executor; }
public void execute(Runnable command) { e.execute(command); }
public void shutdown() { e.shutdown(); }
public List<Runnable> shutdownNow() { return e.shutdownNow(); }
public boolean isShutdown() { return e.isShutdown(); }
public boolean isTerminated() { return e.isTerminated(); }
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
return e.awaitTermination(timeout, unit);
}
public Future<?> submit(Runnable task) {
return e.submit(task);
}
public <T> Future<T> submit(Callable<T> task) {
return e.submit(task);
}
public <T> Future<T> submit(Runnable task, T result) {
return e.submit(task, result);
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
return e.invokeAll(tasks);
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException {
return e.invokeAll(tasks, timeout, unit);
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
return e.invokeAny(tasks);
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return e.invokeAny(tasks, timeout, unit);
}
}
也是Executors的内部类,这里有个设计概念,代理。
把被代理对象ExecutorService作为私有成员变量,然后仅暴露想暴露的方法。(学习了,自己以后开发过程中也可以这么用)
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());}
按需自动扩容线程池。核心线程数为0,keepAliveTime为60s,队列为同步队列。适合执行时间短,任务量大的场景。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
/**
* Creates a new {@code ScheduledThreadPoolExecutor} with the
* given core pool size.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
new DelayedWorkQueue());
}
可以创建一个延迟或周期执行任务的线程池。(具体实现还没细究)
总结一下,几种线程池对于使用方面还是比较容易区分,但是具体该配置多少线程数,这个需要实践才能得出结果(基础环境和业务环境千变万化)。一般可以根据任务的类型类配置(网上的得来的经验,参考一下,使用的时候还是得多多调试。):
CPU密集型型的任务,就要压榨CPU,不要频繁切换,参考值:cpu核心数+1
IO密集型的人物,参考值:2*cpu核心数
最后再强调一下,不要偷懒,一定要经过调试再选择正如的线程数量。