concurrent-线程池
ThreadPoolExecutor
public class ThreadPoolExecutor extends AbstractExecutorService {
//低29位表示线程池的数量,高3位表示线程池状态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//线程池5种状态
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;//销毁
/***
* 用来存放请求任务,支持四种阻塞队列:
* ArrayBlockingQueue:有界阻塞队列
* LindkedBlockingQueue:无界阻塞队列,
* SynchronousQueue:不存储元素的阻塞队列,请求来了直接交给线程池处理,直到达到最大线程数后执行拒绝策略
* priorityBlockingQueue:具有优先级的阻塞队列
*
*/
private final BlockingQueue<Runnable> workQueue;
//每个worker代表一个线程
private final HashSet<java.util.concurrent.ThreadPoolExecutor.Worker> workers =
new HashSet<java.util.concurrent.ThreadPoolExecutor.Worker>();
//核心线程数
private volatile int corePoolSize;
//最大线程数
private volatile int maximumPoolSize;
//核心线程外其他线程的存活时间
private volatile long keepAliveTime;
/***
* 拒绝策略,支持四种拒绝策略:
* AbortPolicy抛出异常,默认的策略
* DiscardPolicy丢弃任务
* CallerRunsPolicy用调用线程来执行任务、
* DiscardOldestPolicy删除阻塞队列中最靠前的任务
*
*/
private volatile RejectedExecutionHandler handler;
private static final RejectedExecutionHandler defaultHandler =
new java.util.concurrent.ThreadPoolExecutor.AbortPolicy();
}
1、首先判断线程池数量是否达到核心线程数,如果没达到则创建新的线程(这一部需要获取全局锁)
2、如果达到核心线程数,则将任务放入到阻塞队列中
3、如果阻塞队列满了,线程池线程数量没还没达到最大线程数,则继续创建新的线程(这一步需要获取全局锁)
4、如果线程池数量也达到了最大线程数,则执行拒绝策略。
这么设计的原因是:正常情况下,大多数的请求过来时都会执行第2步,这一步是不需要获取全局锁的,而1、3步都是要获取全局锁的。
线程池的创建有两种方式,一种是通过工具类Executors封装好的四种方式创建,还有一种是直接通过ThreadPoolExecutor的构造器来创建,推荐使用第2种方式。
Executors类封装了四种类型的线程池:
1、newFixedThreadPool
Executor e = Executors.newFixedThreadPool(10);
底层调用的是
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2、newCachedThreadPool:适合线程并发量高、执行时间短的场景
Executor e = Executors.newCachedThreadPool();
底层调用的是
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
3、newSingleThreadExecutor
Executor e = Executors.newSingleThreadExecutor();
底层调用的是
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
4、newScheduledThreadPool
Executor e = Executors.newScheduledThreadPool(10);
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
有两种方式向线程池提交任务,一种是不带返回值的,一种是带返回值的
不带返回值:提交的任务需要实现runable接口。
带返回值:提交的任务需要提交callable接口,返回future对象异步接收返回信息。
public static void main(String[] args) {
ThreadPoolExecutor t = new ThreadPoolExecutor(100,200,
60L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>());
t.execute(new Runnable() {
@Override
public void run() {
System.out.println(123);
}
});
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);//设置默认拒绝策略
}
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
到现在线程池初始化完成,接下来就是添加任务方法
先总结一下流程:
- 请求任务过来的时候,首先判断线程池数量是否达到核心线程数,如果没达到则调用addWorker方法直接创建一个线程来执行请求任务。
- 如果达到核心线程数,则将请求放入到阻塞队列中
- 如果阻塞队列满了并且线程数量还没达到最大线程数,则继续调用addWorker,传参指明非核心线程。
- 其余情况执行拒绝策略。
addWorker方法:
- 通过cas失败循环重试更新ctl
- 创建一个worker,将请求task封装进去,然后将worker放入线程池中,并且调用worker的start方法。 注意:向线程池添加worker是通过ReentrantLock锁住的。
worker的run方法:
- 该方法开启一个while循环,如果当前worker有任务,则直接执行任务,如果当前worker没任务,则从队列中获取任务,直到队列也没任务,或者非核心线程达到超时时间,中断循环。
- 执行任务即调用具体任务的run方法
- 在while循环中断之后,判断,如果是非核心线程,则更新ctl,同时将线程从池中移除。否则继续调用addWorker方法
为什么用ctl一个变量装两个属性?
这样可以通过cas更新ctl。
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
//如果线程池数量小于核心线程数,创建新的线程执行任务
if (addWorker(command, true))//入口1
return;
c = ctl.get();
}
//如果线程池的数量大于等于核心线程数,将请求任务放入到阻塞队列中成功
if (isRunning(c) && workQueue.offer(command)) {
//任务放入到阻塞队列后要做两个检查
int recheck = ctl.get();
//首先检查:如果线程不是running‘状态则移除该请求任务,移除成功执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);//入口2
//如果线程池数量为0,要创建一个线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//线程池线程数量达到核心线程数,将请求放入阻塞队列失败(阻塞队列也满了),继续创建新的线程执行任务
else if (!addWorker(command, false))
reject(command);
}
有两个方法需要进一步分析:
1、addWorker:向线程池添加新的线程
2、reject:执行拒绝策略
addWorker:该方法首先更新线程池数量,然后创建worker放入线程池,并且调用其start方法
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
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;
}
}
/***
* 上半部分代码实现的是:通过cas失败循环重试更新线程池数量。
* 下半部分代码实现的是:创建一个worker,将请求task封装进去,
* 然后将worker放入线程池中,并且调用worker的start方法。
* 注意:向线程池添加worker是通过ReentrantLock锁住的。
*/
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 {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())
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;
}
worker添加到线程池后start方法也被调用了,接下来看worker的run方法:开启个循环不停的从队列中拿task,然后调用task的run方法,真正执行请求任务。
public void run() {
runWorker(this);
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock();
boolean completedAbruptly = true;//该字段表示worker线程是否异常退出 true为是
try {
//这个while循环实现了线程池中线程的循环逻辑
//线程池中的线程会不停的循环从队列中拿取task,然后调用task的run方法。
//此动作不停的进行,直到从队列中获取不到task为止
while (task != null || (task = getTask()) != null) {//入口
w.lock();
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
//调用task的run方法执行请求任务
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;//走到这里证明worker线程不是异常退出,这里更新标识为false
} finally {
//worker已经执行完毕,这个方法要对他进行回收处理
processWorkerExit(w, completedAbruptly);//入口2
}
}
private Runnable getTask() {
/***
* 该方法实现的逻辑:首先这个方法是在一个worker线程中执行的
* 1、开启一个循环不停的从队列中拿task
* 2、每次拿之前要判断一下当前线程池状态是否正常,如果不正常直接返回
* 3、如果队列一直为空,worker线程拿不到task,难道一直等待吗?
* 根据线程数量是否达到核心线程数、核心线程数是否会超时等属性。判断等待时间。
*/
boolean timedOut = false;
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//以下两种个情况代表着不能继续执行任务了
//1、线程池为stop、清空、销毁状态
//2、线程池为shutdown状态并且队列为空
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();//线程池数量-1,直接返回
return null;
}
int wc = workerCountOf(c);
// 如果设置了核心worker也会超时或者当前正在运行的worker数量超过了corePoolSize,
// 就要根据时间判断是否要销毁线程了
//其实就是从队列获取任务的时候要不要设置超时时间,如果超过这个时间队列还没有任务进来,就会返回null
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//如果上一次循环从队列获取到的为null,这时候timedOut就会为true了
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
//最后如果没设置成功,就进入下一次循环,说不定下一次worker的数量就没有超过corePoolSize了,
// 也就不用销毁worker了
if (compareAndDecrementWorkerCount(c))return null;
continue;
}
try {
//如果要设置超时时间,就设置一下咯
//过了这个keepAliveTime时间还没有任务进队列就会返回null,那worker就会销毁
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
//如果r为null,就设置timedOut为true
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
private void processWorkerExit(ThreadPoolExecutor.Worker w, boolean completedAbruptly) {
//当一个worker执行完毕之后会调用这个方法。
if (completedAbruptly)
decrementWorkerCount();//异常退出执行这个方法
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);//将worker从hashset中移除
} finally {
mainLock.unlock();
}
tryTerminate();//初始化阶段此方法什么也没做
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);//初始化阶段会走到这里
}
}
接下来是reject方法,几种拒绝策略:
AbortPolicy:直接抛出一个异常
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
CallerRunsPolicy:直接由请求的线程来执行
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
DiscardOldestPolicy:从队列中拿出一个任务丢弃掉
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
DiscardPolicy:什么事情也不做
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
ScheduledThreadPoolExcutor
jdk有两种定时器:Timer、ScheduledThreadPoolExcutor。
Timer缺点很明显:单线程、对服务器时间敏感。
总结:该类底层是依赖线程池ThreadPoolExcutor来实现的,自己实现了一个delayBlockingQueue,把请求数据封装到RunnableScheduledFuture对象中,该对象包含了计算好的下次执行时间,执行周期等属性。队列保证每次出队的数据是下次执行时间最靠前的数据,线程池中线程循环从队列中获取数据的时候会判断一下是否到达执行时间。
队列的数据结构如下图:
每次出队的数据为顶点,是下次需要执行的节点。每次添加数据入队的时候会放在最低端,然后和父节点比较下次执行时间,如果大于则调换父子节点,如此循环。(堆排序)
该类核心方法有三个:
1、只执行一次
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit)
2、周期执行,根据上次执行任务前系统时间+执行周期计算下次执行时间
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit)
3、根据上次执行任务完系统时间+执行周期计算下次执行时间
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit)
参数说明:
1、 long initialDelay:首次执行延迟时间
2、long period:后续周期执行延迟时间
使用示例:
static ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
public static void main(String[] args) {
Task t = new Task();
// scheduledThreadPool.schedule(t,2000, TimeUnit.MILLISECONDS);//周期执行,两秒周期
// scheduledThreadPool.scheduleWithFixedDelay(t,2000,1, TimeUnit.MILLISECONDS);
scheduledThreadPool.scheduleAtFixedRate(t,2000,2000, TimeUnit.MILLISECONDS);
}
static class Task implements Runnable{
@Override
public void run() {
System.out.println("23");
}
}
内部定义了一个DelayedWorkQueue,该队列会按照距离下次执行时间进行排序。
在分析代码之前先总结一下流程:
1、首先根据当前时间(或者上次执行时间)和延迟时间计算出执行时间
2、然后把这些请求数据封装到ScheduledFutureTask对象中。
3、然后把ScheduledFutureTask添加到队列中
4、判断线程池数量是否达到核心线程数,如果达到核心线程数则什么也不做
5、如果没达到核心线程数,则执行addWorker方法创建一个worker,将task封装到worker中,同时将worker放入到线程池中,然后调用worker的start方法
worker.run方法的逻辑是:
1、如果task和getTask都为null(预热阶段),则直接执行步骤5(这里在一个循环中,保证线程池中的线程不停的从队列中拿任务执行)
2、如果有一个不为null,则执行相应task的run方法
以scheduleAtFixedRate方法为例
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (period <= 0)
throw new IllegalArgumentException();
//计算出下次执行时间,然后将这些数据封装到RunnableScheduledFuture对象中
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
//将该任务放入到延迟队列中
delayedExecute(t);
return t;
}
到现在数据已经被封装到RunnableScheduledFuture中了,接下来将该任务放入到延迟队列中
private void delayedExecute(RunnableScheduledFuture<?> task) {
if (isShutdown())
reject(task);
else {
super.getQueue().add(task);//添加到队列
if (isShutdown() &&//添加完之后会再次做一些异常校验,如果校验不过则把任务在移除
!canRunInCurrentRunState(task.isPeriodic()) &&
remove(task))
task.cancel(false);
else
ensurePrestart();
}
}
void ensurePrestart() {
int wc = workerCountOf(ctl.get());
if (wc < corePoolSize)
addWorker(null, true);
else if (wc == 0)
addWorker(null, false);
}
addWorker是ThreadPoolExcutor来实现的。
addWorker实现的逻辑:线程池执行一个请求,在上边首先是将请求放入到队列中,然后在addWorker中,判断线程池是否达到了核心线程数,如果没达到,则创建一个worker,然后将该worker放入到线程池的HashSet中,然后调用worker的start方法。
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
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 {
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;
}
worker的run方法的逻辑:
1、如果worker中的task为null,并且getTask(从队列中获取任务)也为null(线程没达到核心线程数预热阶段),则直接结束
2、如果worker中的task不为null,或者getTask不为null(线程池已经达到核心线程数,从队列拿请求执行阶段),则调用task的run方法(根据不同task执行不同的逻辑)
3、最后在processWorkerExit()方法中循环执行addWorker方法来实现线程池不停从队列中拿请求执行的逻辑。
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
上边都是ThreadPoolExcutor的代码,接下来继续ScheduleThreadPoolExcutor。
这里执行的是ScheduledFutureTask的run方法
public void run() {
// 是否是周期性任务
boolean periodic = isPeriodic();
// 当前线程池运行状态下如果不可以执行任务,取消该任务
if (!canRunInCurrentRunState(periodic))
cancel(false);
// 如果不是周期性任务,调用FutureTask中的run方法执行
else if (!periodic)
ScheduledFutureTask.super.run();
// 如果是周期性任务,调用FutureTask中的runAndReset方法执行
// runAndReset方法不会设置执行结果,所以可以重复执行任务
else if (ScheduledFutureTask.super.runAndReset()) {
// 计算下次执行该任务的时间
setNextRunTime();
// 重复执行任务
reExecutePeriodic(outerTask);
}
}
实现周期执行:计算出下次执行时间,然后创建一个task放入队列中,执行ensurePrestart方法。
void reExecutePeriodic(RunnableScheduledFuture<?> task) {
if (canRunInCurrentRunState(true)) {
super.getQueue().add(task);
if (!canRunInCurrentRunState(true) && remove(task))
task.cancel(false);
else
ensurePrestart();
}
}