Executor线程池原理详解
Posted yatou-blog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Executor线程池原理详解相关的知识,希望对你有一定的参考价值。
线程池
线程池的目的就是减少多线程创建的开销,减少资源的消耗,让系统更加的稳定。在web开发中,服务器会为了一个请求分配一个线程来处理,如果每次请求都创建一个线程,请求结束就销毁这个线程。那么在高并发的情况下,就会有大量线程创建和销毁,这就会降低系统的效率。线程池的诞生就是为了让线程得到重复使用,减少了线程创建和销毁的开销,减少了线程的创建和销毁自然的就提高了系统的响应速度,与此同时还提高了线程的管理性,使线程可以得到统一的分配,监控和调优。
线程创建和销毁为什么会有开销呢,因为我们java运行的线程是依赖于计算机内核的核心线程的。java创建的线程是用户层的线程,要依赖于线程调度去是用内核层的线程来执行,在执行销毁的时候会通过TSS在用户层和核心层的切换,这个切换就是很大的一笔开销。具体结构如下图:
线程实现方式
线程主要通过实现Runnable或者Callable接口来实现.Runnable与Callable的区别在于后者有返回值,但是前者没有返回值。
public interface Runnable {
public abstract void run();
}
publuic interface Callable<V>{
V call() throws Exception;
}
下面我们来看一下测试代码:
package com.test.excutor;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
public class ExcutorTest {
public static void main(String[] args) {
Thread t =new Thread( new RunTask());
t.start();
FutureTask<Object> ft=new FutureTask<Object>(new CallTask());
Thread f=new Thread(ft);
f.start();
try {
System.out.println("callTask output:"+(String)ft.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class RunTask implements Runnable{
@Override
public void run() {
System.out.println(" RunTask Thread Name is "+Thread.currentThread().getName());
}
}
class CallTask implements Callable<Object>{
@Override
public Object call() throws Exception {
TimeUnit.SECONDS.sleep(1);
//System.out.println("This is callTask");
return "callTask answer";
}
}
//运行结果:
RunTask Thread Name is Thread-0
callTask output:callTask answer
什么时候使用线程池:
1、单个任务处理时间比较短
2、需要处理的任务数量很大
Executor框架
Executor接口是Executor框架的一个最基本的接口,Executor框架的大部分类都直接或间接地实现了此接口。
它只有一个方法
void execute(Runnable command): 在未来某个时间执行给定的命令。该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。
以下是框架图:
从以上图中可以看出ExecutorService就是继承了Executor接口的一个重要接口类。在这个接口类中定义了线程池的具体行为:
11、invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException
:执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。返回列表的所有元素的 Future.isDone() 为 true。注意,可以正常地或通过抛出异常来终止已完成 任务。如果正在进行此操作时修改了给定的 collection,则此方法的结果是不确定的。
12、invokeAll(Collection<? extends Callable<T>> tasks, long timeout,TimeUnit unit) throws InterruptedException:超时等待,同上。
13、invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException,ExecutionException
:与 invokeAll的区别是,任务列表里只要有一个任务完成了,就立即返回。而且一旦正常或异常返回后,则取消尚未完成的任务。
14、invokeAll(Collection<? extends Callable<T>> tasks,long timeout,TimeUnit unit) throws InterruptedException
: 超时等待,同上。
线程池的创建
线程池的创建是通过Executors来创建的。比如说你需要创建一个固定大小的线程池我们可以使用Executors.newFixedThreadPool(n)来实现。当然还有很多其他的方法
我们来看一下:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
//只允许一个线程执行
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
//可以无限的创建线程,会把创建的线程放在一个特殊的队列中去排队
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
//定时线程池
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
newFixedThreadPool(int nThreads)方法中的几个参数如下:
nThreads:核心线程数量
nThreads:最大线程池的数量 0L: 如果线程已经工作完毕,没有任务调用,该线程最大的存活时间
TimeUnit.MILLISECONDS:计时时间,单位为ms new LinkedBlockingQueue<Runnable>():当核心线程全都在工作,没有空闲,此时会将多余的线程放到阻塞队列中排队,当核心线程执行完成以后再从阻塞队列当中拿出来继续执行。
下面我们来跑一段代码:
public class ExecutorTest {
public static void main(String[] args) {
Thread t =new Thread( new RunTask());
t.start();
FutureTask<Object> ft=new FutureTask<Object>(new CallTask());
Thread f=new Thread(ft);
f.start();
try {
System.out.println("callTask output:"+(String)ft.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Executor Test Start");
ExecutorService exe=Executors.newFixedThreadPool(5);
for(int i=0;i<20;i++){
exe.execute(new RunTask());
exe.submit(new RunTask());
}
/*//验证线程创建慢
if(exe.isShutdown()){
System.out.println("Thread 已经stop,等待线程创建");
exe.execute(new RunTask());
System.out.println("Executor Test end");
}else{
exe.shutdownNow();
} */
}
}
//结果如下:
RunTask Thread Name is Thread-0
callTask output:callTask answer
Executor Test Start
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-4
RunTask Thread Name is pool-1-thread-3
RunTask Thread Name is pool-1-thread-2
根据代码的测试结果我们可以看出线程1,线程2已经跑了好久了线程3,4,5才创建好,这个从侧面验证了线程的创建是很耗时的。
具体我们来看一下线程池类的工作流程和工作原理:
线程池线程的大小=核心线程+非核心线程
线程池的工作原理如上图所示已经非常清晰了,文字描述一下具体步骤:
1、启动线程池执行任务的时候先创建核心线程来执行任务;
2、核心线程数量创建达到规定值以后,还有任务没有线程执行的话就将任务放到阻塞队列中取排队等待;
3、等到队列排满了还有线程需要执行的话就创建非核心线程;
4、非核心线程还不够执行任务的话就直接执行拒绝策略。
线程池的重要属性
在ThreadPoolExecutor中有以下几个比较重要的属性:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
ctl是一个控制状态,对线程池的运行状态和线程池中的有效线程数量进行控制的一个字段。它包含两部分的信息:
1、线程池的运行状态(runState),高3位保存运行状态.相关方法是private static int runStateOf(int c){ return c& ~CAPACITY;}
2、线程池内的有效线程数量(workerCount),ctl 是个Integer类型的数据,低29位保存;相关方法是 private static int workerCountOf(int c){ return c& ~CAPACITY;}
3、控制状态的方法是:private static int ctlOf(int rs,int wc){return rs|wc;}
private static final int COUNT_BITS = Integer.SIZE - 3;( Integer.SIZE=31)
count_bits=29,1<<29,也就是说workerCount最大值(2^29)-1(约5亿)
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
容量=2^29-1。
线程池的5种状态:
1、RUNNING:private static final int RUNNING = -1 << COUNT_BITS;高3位111
状态说明:线程池处于Running状态的时候,能够接收新的任务,并且对已添加的任务进行处理,任务不能大于规定的最大任务数;
状态切换:线程池的初始状态是RUNNING,也就是说线程池一旦被创建就处于RUNNING状态,并且线程池中的任务数量为0;
2、SHUTDOWN:private static final int SHUTDOWN = 0 << COUNT_BITS;高三位为000
状态说明:线程池处于SHUTDOWN状态的时候,不接收新的任务,但是可以处理已经添加的任务;
状态切换:调用线程池的shutDown()方法的时候,线程状态由RUNNING ---->>>>SHUTDOWN
3、 STOP :private static final int STOP = 1 << COUNT_BITS;高三位为001
状态说明:线程池处于该状态的时候,不接收新的任务,不处理已接收的任务,并且还会中断正在处理的任务,中断并不代表线程被杀死了,并且清空阻塞队列。
状态切换:线程池调用shutDownNow()接口的时候,线程池由RUNNING(SHUTDOWN)-------->>>STOP
4、TIDYING :private static final int TIDYING = 2 << COUNT_BITS;高三位为010
状态说明:当所有的任务已经终止,ctl的值为0,线程池会变成TIDYING状态,当线程池处于该状态的时候会执行钩子函数terminated().terminated()方法在ThreadPoolExecutor中是空的,用户想在线程池变为TIDYING状态的时候处理东西,可以通过重载terminated()方法实现。
状态切换:当线程池处于SHUTDOWN状态,并且阻塞队列中的任务为0,就会SHUTDOWN----->>>TIDYING,当线程池处于STOP状态下,线程池中执行任务数量为0,那么线程池状态STOP----->>>TIDYING.
5、TERMINATED:private static final int TERMINATED = 3 << COUNT_BITS;高三位为011
状态说明:线程池已经彻底终止,就会变成TERMINATED状态
状态切换:线程池处于TIDYING状态,执行完terminated()方法以后,就会实现TIDYING----->>>TERMINATED
进入该状态的条件如下:
1)线程池不是RUNNING状态;
2)线程池不是TIDYING状态或者TERMINATED状态;
3)如果线程池状态是SHUTDOWN,并且阻塞队列中的任务数量为0;
4)workerCount=0;
5)设置TIDYING状态成功;
线程池状态的切换如下图所示:
线程池的默认实现-ThreadPoolExecutor
创建方法:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
任务的提交:
public void execute():提交任务没有返回值
参数解释:
corePoolSize:核心线程数,当提交一个任务的时候,创建一个核心线程,直到达到核心线程的最大数量。
maximumPoolSize:线程池最大容量,即该线程池最大允许的线程数量,当当前的阻塞队列满的时候,还有任务继续提交,则创建新的非核心线程继续执行,但是线程总数不能大于最大容量maximumPoolSize。
keepAliveTime:空余线程存活时间,即当线程池的核心线程数达到最大的时候,没有新的任务提交,那么非核心线程不会立即销毁,而是等待,等待时间大于keepAliveTime才会进行销毁。
unit:keepAliveTime的时间单位
BlockingQueue<Runnable> workQueue:阻塞队列,当核心线程数达到最大值的时候,还有新任务提交则将这些线程放到该队列中进行排队等待,提交的任务必须实现Runnable接口。除了这个阻塞队列以外,还有以下的阻塞队列:
1、ArrayBlockingQueue:基于数组结构的有界队列,遵循FIFO原则。
2、LinkedBlockingQueue:基于链表的有界队列,遵循FIFO原则,吞吐量高于ArrayBlockingQueue。
3、SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
4、priorityBlockingQuene:具有优先级的无界阻塞队列;
threadFactory:用来创建线程,默认使用Executors.defaultThreadFactory() 来创建线程使用默认的ThreadFactory来创建线程时,会使新创建的线程具有相同的NORM_PRIORITY优先级并且是非守护线程,同时也设置了线程的名称。
RejectedExecutionHandler handler:线程池的拒绝策略,线程池的拒绝策略有4种:
1、AbortPolicy:直接抛出异常,这是默认策略;
2、CallerRunsPolicy:用调用者所在的线程来执行任务:
3、DiscardOldestPolicy:抛弃阻塞队列中最靠前的任务,并执行当前任务;
4、DiscardPolicy:直接丢弃任务
线程池监控:
public int getPoolSize():获取当前线程池的大小
public int getActiveCount():线程池中正在执行任务的线程数量
public int getLargestPoolSize():获取线程池中出现出现过的最大线程数量
public long getTaskCount():获取线程池已执行和未执行的线程总数
public long getCompletedTaskCount():获取已经完成的任务的数量
详细的工作原理图请参考线程池的执行过程图。线程池中创建线程并执行用的方法是addWorker(Runnable firstTask, boolean core)方法。其中的firstTask用于指定新增线程执行的第一个任务,如果没有任务执行可以为null;boolean core 主要是判断当前线程池中活动的核心线程数是否达到最大,如果达到最大的话就创建非核心线程,该值为false,如果没有达到最大核心线程数量,则为true,创建核心线程。
private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) {
//获取线程池的运行状态 int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary.当rs的值>=SHUTDOWN说明线程池不再接收新的任务了,然后判断以下三个条件:
1、rs==SHUTDOWN 表示此时是关闭状态,不再接收新的任务,但是可以继续处理阻塞队列中的任务
2、firstTask==null:firstTask为空
3、!workQueue.isEmpty():阻塞队列不为空
以上三个条件有一个不满足,则返回false,不创建新线程。
if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; for (;;) { int wc = workerCountOf(c);//获取工作线程数 if (wc >= CAPACITY || //判断线程数是否大于初始容量,即ctl的低29位都为1 wc >= (core ? corePoolSize : maximumPoolSize))//根据core的值,为true,则将wc与核心线程数来比较,如果为false,就跟线程池最大容量相比 return false; //如果以上两个条件满足则说明线程池已经满了,不能创建新线程,返回false if (compareAndIncrementWorkerCount(c))//尝试增加workerCount,增加成功跳出此层for循环 break retry; c = ctl.get(); // Re-read ctl 增加workerCount失败,重新获取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);//根据firstTask创建worker final Thread t = w.thread;//每个worker创建一个线程 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 || //状态是RUNNING状态 (rs == SHUTDOWN && firstTask == null)) {//或者状态处于SHUTDOWN,且firstTask为空(SHUTDOWN不接收新任务但是任然执行队列中的任务) if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); workers.add(w);//向线程池中添加线程,workers是一个HashSet int s = workers.size();//获取线程池数量 if (s > largestPoolSize)//largestPoolSize记录着线程池中出现过的最大线程数量,如果此时的线程数量大于之前的largestPoolSize,则重新赋值 largestPoolSize = s; workerAdded = true;//线程添加成功 } } finally { mainLock.unlock(); } if (workerAdded) { t.start();//运行线程 workerStarted = true;//线程运行成功 } } } finally { if (! workerStarted) addWorkerFailed(w); } return workerStarted; }
Worker类
worker类封装了线程池中的每一个线程,线程池ThreadPool本质上维护的就是worker类,该类继承了AQS,实现了Runnable接口。worker类包含属性thread,firstTask,completedTasks三个属性。
firstTask:保存传入的任务
thread:执行任务创建的线程,在构造方法中通过getThreadFactory().newThread(this)创建线程,传入的是this,其实worker本身就实现了Runnable接口,所以worker本身就是一个线程,在线程启动的时候就会调用worker类的run方法。Worker类继承AQS,而不是ReetrantLo是因为AQS使用的是独占锁,是不可以重入的。lock一旦获取到了独占锁,就表明线程在运行中,就不应该中断。如果线程不是在独占状态,那么就说明该线程是空闲线程,可以进行中断。线程池在执行shutdown方法或tryTerminate方法时会调用interruptIdleWorkers方法来中断空闲的线interruptIdleWorker方法会使用tryLock方法来判断线程池中的线程是否是空闲状态;之所以设置为不可重入,是因为我们不希望任务在调用像setCorePoolSize这样的线程池控制方法时重新获取锁。如果使用ReentrantLock,它是可重入的,这样如果在任务中调用了如setCorePoolSize这类线程池控制的方法,会中断正在运行的线程。final Thread thread; Runnable firstTask; volatile long completedTasks; /** * Creates with given first task and thread from ThreadFactory. * @param firstTask the first task (null if none) */ Worker(Runnable firstTask) { setState(-1); // inhibit interrupts until runWorker是因为AQS中默认的state是0,如果刚创建了一个Worker对象,还没有执行任务时,这时就不应该被中断
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this); }
在上述内容中我提到了,线程池中创建的线程主要是worker,线程的执行也是worker,worker在执行的时候调用的run方法,代码里的run方法调用的就是runWorker()方法,下面我们就来解读一下runWorker()方法:
final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts 将state设置为0 boolean completedAbruptly = true; try { while (task != null || (task = getTask()) != null) {//任务不为空进入循环 getTask()从阻塞队列中获取任务 w.lock();//线程运行上锁 /*如果线程正在停止,保证线程处于中断状态,如果不是的话保证当前线程不是中断状态;
这里需要考虑执行If语句期间也执行了SHUTDOWNNOW方法,将状态直接置为STOP,同时还会中断线程池中的所有线程wt.interrupted()来判断是否中断,是为了确保在RUNNING和SHUTDOWN状态的时候是处于非中断状态。
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++;//完成任务数量+1 w.unlock();//释放锁 } } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); } }
根据上述的代码以及注释,现在来总结一下,线程运行的runWorker()方法总体的运行流程如下:
1、while循环,加锁,从阻塞队列中获取任务(getTask())
2、如果线程正在停止,保证当前线程处于中断状态,如果不是则保证当前线程不是中断状态
3、运行任务内容
4、任务运行完成,释放锁。
5、当获取的任务为空跳出while循环,执行 processWorkerExit(w, completedAbruptly)方法。
除了runWorker之外,上述还有提到getTask(),从阻塞队列中获取任务:
private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out?表示上次从阻塞队列中取值的时候是否有超时 for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary.线程池是SHUTDOWN以上的状态的时候,判断线程池是否停止,或者阻塞队列是否为空,如果都是的话那么workerCout减1
并且返回空值。因为在线程池在SHUTDOWN以上的状态的时候应该,不接收新的任务,也不允许往阻塞队列当中添加新的任务。
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; } //获取线程的数量 int wc = workerCountOf(c); // Are workers subject to culling?timed判断是否需要进行超时控制。allowCoreThreadTimeOut参数是允许核心线程超时标识,默认是false.同时也判断当前线程
数量是否是大于核心线程的数量的。
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; //当当前工作线程数量大于规定线程池最大容量,或者超时控制为真的时候,同时判断当前线程数大于1或者阻塞队列是空的两个条件,当两个条件中有一个为真,那么将workerCount-1,
c成功的话就返回null值,失败的话就重试。该判断比较重要,主要是当线程池中的线程数量处于大于corePoolSize,但是又小于maximumPoolSize的状态下,获取任务超时,说明阻塞队列为空,也就说明
现在线程池不需要那么多线程来执行任务,需要把corePoolSize的线程销毁,让线程数量维持在corePoolSize即可。
if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; } try { Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();//timed =true 那么就通过阻塞队列的poll方法进行超时控制,否则的话就从队列中获取任务。 if (r != null) return r;//获取任务成功,返回获取的任务。 timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
在runWorker()方法中,getTask失败,是会跳出while 循环,执行processWorkerExit()方法:
private void processWorkerExit(Worker w, boolean completedAbruptly) { if (completedAbruptly) // 如果执行线程出现异常,那么workerCount-1 decrementWorkerCount(); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { completedTaskCount += w.completedTasks;//completedTaskCount统计完成任务的线程数,同时移除已经执行完任务的worker workers.remove(w); } finally { mainLock.unlock(); } tryTerminate(); int c = ctl.get(); if (runStateLessThan(c, STOP)) {//线程的状态是RUNNING或者SHUTDOWN进行以下判断 if (!completedAbruptly) {//如果线程非异常结束进行以下操作 int min = allowCoreThreadTimeOut ? 0 : corePoolSize;//如果允许核心线程超时,那么min=0,如果不允许那么min=核心线程的数量 if (min == 0 && ! workQueue.isEmpty()) min = 1;//如果核心线程允许超时,min==0且阻塞队列不为空的,min=1,即至少保证线程池中有1个worker if (workerCountOf(c) >= min) return; // replacement not needed } addWorker(null, false);//如果worker异常结束那么就addWorker } }
综上所述,线程池中线程的执行过程大致如下:
也就是说,当线程池在执行Executors.newFixedThreadPool(n).execute(Runnable)的方法的时候,就进入到线程池ThreadPoolExecutor中去执行execute(Runnable)方法,该方法主要addWorker(),在执行addWorker的时候,worker类会创建线程getThreadFactory().newThread(this),创建好线程以后,线程会启动,t.start()实际调用的就是worker类中的run()方法,该方法的实质是运行runWorker()方法,在执行该方法的时候就会从阻塞队列中获取任务,获取任务成功以后执行线程,完成任务即可。
以上是关于Executor线程池原理详解的主要内容,如果未能解决你的问题,请参考以下文章