本文参考
Java线程池---addWorker方法解析
Java线程池ThreadPoolExecutor实现原理
线程池如何复用
Executor(Interface):
执行提交的线程任务的对象。这个接口提供了一种将任务提交与每个任务将如何运行实现了分离,包括线程使用、调度等细节。该接口只定义了一个execute()方法。
// 用来执行一个指令任务,这个任务可能在一个新的线程中执行,可能在线程池已有的线程中执行,也可能在当前线程执行,由Executor接口的实现方决定;
void execute(Runnable command);
ExecutorService(Interface):
提供用于管理终止的方法如 shutDown()和shutDownNow()用于关闭线程池的方法以及判断线程池是否关闭的方法如,isShutdown(),isTerminated()的方法,还提供了involveAll()和submit()
// 关闭线程池,不接受新的任务,继续执行已经提交的任务,但是不等待已经提交的任务完成,使用awaitTermination接口
void shutdown();
// 判断当前线程池是否被关闭
boolean isShutdown();
// 判断是否所有提交的任务在showDown()后已经执行完毕
boolean isTerminated();
// 执行一个任务,并且返回这个任务的Future对象,用于表示这个任务的挂起结果
Future submit(Callable task);
// 执行许多任务,返回任务的results
T invokeAny(Collection extends Callable> tasks)
throws InterruptedException, ExecutionException;
// 执行许多任务,返回许多Future(包含status and results)
List> invokeAll(Collection extends Callable> tasks)
throws InterruptedException;
Callable:
和Runnable类似,是java开启子线程的一种方式,用Callable开启的线程可以取消执行;
- call():类似run方法
Future:
一个代表线程执行结果的类,可以用来判断线程是否运行结束,主动终止线程
- get():等待线程执行完毕,返回线程的结果
- cancel():停止线程
- isDown():判断线程是否结束
- isCancel():判断线程是否取消
ThreadPoolExecutor:
AbstractExecutorService的实现类,java线程池的核心实现类,下面我们重点看这个类;
重要变量:
// 构造方法中的参数
private volatile int corePoolSize;// 核心线程数
private volatile int maximumPoolSize;// 最大线程数
private volatile long keepAliveTime; // 空闲线程的可存活时间
private final BlockingQueue workQueue; // 工作队列,存放任务的数据结构
private volatile ThreadFactory threadFactory; // 线程工厂,创建新线程的工厂
private volatile RejectedExecutionHandler handler; // 抛弃线程策略
// 线程池状态变量(这是一个atomic integer)
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
深入解析Java AtomicInteger 原子类型
在线程池的内部,封装了一个Worker对象,Worker对象是线程池维护的用来处理用户提交的任务的线程;
// worker的本质是一个任务
private final class Worker extends AbstractQueuedSynchronizer implements Runnable
execute():
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
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) { // 如果任务可以成功的加入阻塞队列
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command)) // 双重检查:如果线程池被关闭并且任务成功的从阻塞队列中取出,就放弃这个任务
reject(command);
else if (workerCountOf(recheck) == 0) // 如果当前的线程数为0(线程池没有被关闭),开启新线程执行
addWorker(null, false);
}
else if (!addWorker(command, false)) // 如果无法创建新线程去执行这个任务 ,也放弃这个任务
reject(command);
}
execute()的流程分为三部分(上面有详细的英文注释):
- 如果比正在运行的核心线程数量小,则尝试开启一个新线程,将这个任务作为他的第一个任务,并且调用addWorker()去检查 runState 和workerCount,避免不必要的错误警报
- 如果一个任务可以成功的加入阻塞队列中排队,我们需要再次双重检验是否我们需要创建一个新的线程,如果线程池被关闭(由于这个任务的某个逻辑导致)或者现存的线程死掉了,我们就调用handler去拒绝这个任务,如果当前线程池没有线程,则创建一个新的线程
- 如果这个线程无法进入阻塞队列又无法创建新线程去执行,我们就用handler丢弃他;
addWorker():
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
// 使用CAS机制轮询线程池的状态,如果线程池处于SHUTDOWN及大于它的状态则拒绝执行任务
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 如果是STOP,TIDYING,TERMINATED状态的话,则会返回false,如果现在状态是SHUTDOWN,但是firstTask不为空或者workQueue为空的话,那么直接返回false
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
// 获得当前workerCount,如果当前workerCount大于容量/核心线程数/最大线程数,return false
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 判断当前的线程池状态是否可以创建新的worker,可以的话 break retry,创建新worker
if (compareAndIncrementWorkerCount(c))
break retry;
// CAS的自旋,再次比较ctl,如果不一致retry
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
// 创建新的worker
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
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());
// 在锁内部在进行一次判断是否满足创建worker的条件
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;
}
addWorker:
根据当前线程池的状态和给定的线程数大小(core/maximum)判断是否可以添加一个新的worker,如果可以,创建一个新的worker,当前任务作为他的first task,当线程池被关闭或者工厂创建线程失败,这个方法返回false;
CAS机制
什么是retry?
自带的ThreadPoolExecutor对象
- newFixedThreadPool:一个固定线程数量的线程池:
- newCachedThreadPool:不固定线程数量,且支持最大为Integer.MAX_VALUE的线程数量:
- newSingleThreadExecutor:可以理解为线程数量为1的FixedThreadPool:
- newScheduledThreadPool:支持定时以指定周期循环执行任务:
拒绝策略(当阻塞队列和max都满了做出的处理)
- AbortPolicy:直接抛出异常。
- CallerRunsPolicy:由调用者所在线程来运行任务。
- DiscardOldestPolicy:丢弃队列里最老的一个任务,并执行当前任务。
- DiscardPolicy:丢弃掉当前提交的新任务。
封装的自定义的ThreadPoolExecutor
public class ThreadPoolManager {
/**
* 单例设计模式(饿汉式)
* 单例首先私有化构造方法,然后饿汉式一开始就开始创建,并提供get方法
*/
private static ThreadPoolManager mInstance = new ThreadPoolManager();
public static ThreadPoolManager getInstance() {
return mInstance;
}
private int corePoolSize;//核心线程池的数量,同时能够执行的线程数量
private int maximumPoolSize;//最大线程池数量,表示当缓冲队列满的时候能继续容纳的等待任务的数量
private long keepAliveTime = 1;//存活时间
private TimeUnit unit = TimeUnit.HOURS;
private ThreadPoolExecutor executor;
private ThreadPoolManager() {
/**
* 给corePoolSize赋值:当前设备可用处理器核心数*2 + 1,能够让cpu的效率得到最大程度执行
*/
corePoolSize = Runtime.getRuntime().availableProcessors() * 2 + 1;
maximumPoolSize = corePoolSize; //虽然maximumPoolSize用不到,但是需要赋值,否则报错
executor = new ThreadPoolExecutor(
corePoolSize, //当某个核心任务执行完毕,会依次从缓冲队列中取出等待任务
maximumPoolSize, //5,先corePoolSize,然后new LinkedBlockingQueue(),然后maximumPoolSize,但是它的数量是包含了corePoolSize的
keepAliveTime, //表示的是maximumPoolSize当中等待任务的存活时间
unit,
new LinkedBlockingQueue(), //缓冲队列,用于存放等待任务,Linked的先进先出
Executors.defaultThreadFactory(), //创建线程的工厂
new ThreadPoolExecutor.AbortPolicy() //用来对超出maximumPoolSize的任务的处理策略
);
}
/**
* 执行任务
*/
public void execute(Runnable runnable) {
if (runnable == null) return;
executor.execute(runnable);
}
/**
* 从线程池中移除任务
*/
public void remove(Runnable runnable) {
if (runnable == null) return;
executor.remove(runnable);
}
}
配置线程池
任务的性质:
- CPU密集型(大量计算) :CPU数+1
- IO密集型(磁盘IO,网络IO):CPU数*2+1
线程池如何复用线程?
一个大run()把其它小run()#1,run()#2,...给串联起来了
前提条件:假如coreSize=3,maxSize=10,当前存在线程数是5。
(注意,存在的这5个线程,并不是你执行ExecuteService.execute/submit时的参数,而是为了执行execute/submit的参数所启动的“内部线程”。这个“内部线程”其实是通过ThreadPoolExecutor的ThreadFactory参数生成的线程,而“execute/submit的参数”是执行在这些“内部线程”里面的。)
存在这5个“内部线程”,都访问同一个队列,从队列中去取任务执行(任务就是通过execute/submit提交的Runnable参数),当任务充足时,5个“内部线程”都持续执行。重点是没有任务时怎么办?
没有任务时,这5个“内部线程”都会做下面判断:
如果poolSize > coreSize,那就从队列里取任务,当过了keepaliveTime这么长时间还没有得到任务的话,当前这个“内部线程”就会结束(使用的是BlockingQueue.poll方法)。
如果poolSize <= coreSize,那就以“阻塞”的方式,去从队列里取任务,当得到任务后,就继续执行。这样的话,这个线程就不会结束掉。
如果没有任务可以继续执行了,最后只剩下coreSize那么多的“内部线程”留在线程池里,等待重用。