线程池
简介
线程池(Thread pool)是一种线程使用模式。由于线程过多会带来调度开销,进而影响缓存局部和整体的性能。而线程池维护这多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。(Java线程池相关类是在1.5新增的,属于包rt.jar,java线程池也遵循线程池的核心设计思路,通过复用线程降低线程创建、销毁带来的额资源消耗,同时提供了多种线程池的实现模型,并且允许开发者定制不同线程池)
优势
-
降低资源消耗:
通过重复利用已创建的线程,降低线程创建和销毁造成的消耗,从而提高整体的执行效率
-
可扩展的开发模式:
除JVM提供的三种线程池,另外可以通过实现AbstractExecutorService类定制自己的线程池而支持不同的业务场景
-
提高线程的可管理性:
线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使线程池可以进行统一的分配,调优和监控
原生线程池(UML关系图)
Executor 一个根据一组执行策略调用调度执行和控制的异步任务的框架,目的是提供一种将任务提交与任务如何运行(包括线程使用的细节、调度等)**分离开来的机制,**通常使用Executor而不是显式地创建线程。
JUC的三个Executor接口
Executor
接口只有一个方法对于不同的Executor实现,execute方法可能是创建一个新线程并立即启动,也可能是使用已有的工作线程来运行传入的任务,也可能是根据设置线程池的容量或者阻塞队列的容量来决定是否要将传入的线程放到阻塞队列中,或者拒绝接收传入的线程
void execute(Runnable command);
ExecutorService
提供了管理终止的方法(void shutdown()),以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。 可以关闭 ExecutorService,这将导致其停止接受新任务。关闭后,执行程序将最后终止,这时没有任务在执行,也没有任务在等待执行,并且无法提交新任务。具备管理执行器和任务生命周期的方法,提交任务机制更完善(submit方法,传入Callable接口)
void shutdown();
List<Runnable> shutdownNow();
<T> Future<T> submit(Callable<T> task); //扩展了任务执行的能力,支持多个任务批量执行
ScheduledExecutorService
扩展了ExecutorService,支持Future,可安排在给定的延迟后运行或定期执行的命令.
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);
Executors创建线程池
Executors是一个工具类,用其可以创建ExecutorService、ScheduledExecutorService、ThreadFactory、Callable等对象。它的使用融入到了ThreadPoolExecutor, ScheduledThreadExecutor和ForkJoinPool中
1. newSingleThreadExecutor():
- 创建一个单线程的线程池。此线程保证所有任务的执行顺序是按照任务的提交顺序执行,相当于单线程串行执行所有任务
//创建唯一的工作线程来执行任务,如果线程异常结束,会有另一个线程取代它
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}
//问题: 由于使用了无界队列, 所以SingleThreadPool永远不会拒绝, 即饱和策略失效
2. newFixedThreadPool()
- 创建固定大小的线程池,每次提交一个任务就创建一个线程,知道线程达到线程池最大。线程池的大小一旦达到最大值就会保持不变,如果线程异常,那么线程池会补充一个新线程
//指定工作线程数量的线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
//问题:线程池里的线程数量不超过corePoolSize,这导致了maximumPoolSize和keepAliveTime将会是个无用参数
//由于使用了无界队列,队列容量为Integer.MAX_VALUE, 所以FixedThreadPool永远不会拒绝, 即饱和策略失效
3. newCachedThreadPool()
- 创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,其大小完全依赖于操作系统或JVM能够创建的最大线程大小
//处理大量短时间工作任务的线程池:
//1.试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程
//2.如果线程闲置的时间超过阈值,则会被终止并移除缓存
//3.系统长时间闲置的时候,不会消耗什么资源
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
4. newWorkStealingPool()
- ForkJoinPool是JDK 7加入的一个线程池类,用于并行执行任务的框架。Fork/Join 技术是分治算法(Divide-and-Conquer:某个线程从其他队列里窃取任务来执行)的并行实现,它是一项可以获得良好的并行性能的简单且高效的设计技术。目的是为了帮助我们更好地利用多处理器带来的好处(为了能够被递归的拆解子任务的工作类型量身设计的),使用所有可用的运算能力来提升应用的性能。(把大任务分割成若干个小任务,Fork/Join 把这些子任务分别放到不同的队列里,并为每个队列创建一个单独的线程来执行队列里的任务,并行执行,最终汇总每个小任务结果后得到大任务结果的框架 )
//内部会构建ForkJoinPool,利用working-stealing算法,并行地处理任务,不保证处理顺序
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
5. newScheduledThreadPool() 和new SingleThreadExecutor()
- ScheduledThreadPoolExecutor实现ScheduledExecutorService接口,可安排在给定的延迟后运行命令,或者定期执行命令。需要多个辅助线程时,或者要求 ThreadPoolExecutor 具有额外的灵活性或功能时,此类要优于 Timer
//此线程池支持定时或者周期性执行任务的需求,两者的区别在于单一工作线程还是多个线程
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
ThreadPoolExecutor
介绍
![]() | ThreadPoolExecutor实现的顶层接口是Executor,内部只有一个方法execute(Runable),标识出执行任务这个核心方法。限制了任务类型为:Runable ExecutorService接口扩展了很多能力,比如对线程池的管理。以及扩展了执行任务的能力(submit),支持多个任务批量执行 |
---|---|
AbstractExecutorService 是对 ExecutorService 的抽象类,对任务的执行和调用做了基础的实现,目前都是在对任务的执行做层层抽象,也规范了任务的基础类型(此类使用 newTaskFor() 返回的 RunnableFuture 实现 submit、invokeAny 和 invokeAll 方法,默认情况下,RunnableFuture 是此包中提供的 FutureTask 类)FutureTask 为 Future 提供了基础实现,如获取任务执行结果(get)和取消任务(cancel)等。如果任务尚未完成,获取任务执行结果时将会阻塞。一旦执行结束,任务就不能被重启或取消(除非使用runAndReset执行计算)。 | FutureTask 常用来封装 Callable 和 Runnable,也可以作为一个任务提交到线程池中执行。除了作为一个独立的类之外,此类也提供了一些功能性函数供我们创建自定义 task 类使用。**FutureTask 的线程安全由CAS来保证 (**https://www.pdai.tech/md/java/thread/java-thread-x-juc-executor-FutureTask.html) |
ThreadPoolExecutor实现了AbstractExecutorService接口,也是一个 ExecutorService,它使用可能的几个线程池之一执行每个提交的任务,通常使用 Executors 工厂方法配置。 ThreadPoolExecutor是一个java原生线程池的一个基础实现类,完成了线程池的各种功能,内部维护了存储任务的阻塞队列,以及执行任务的worker线程,还有线程池的相关状态管理及任务管理。同时提供了一些扩展方法,供开发者定制特色能力。
线程池可以解决两个不同问题: 由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行任务集时使用的线程)的方法。每个 ThreadPoolExecutor 还维护着一些基本的统计数据,如完成的任务数
ThreadPoolExecutor构造函数入参
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
};
-
corePoolSize:核心线程数量(默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中数目达到corePoolSize时,就会把到达的任务放到缓存队列中;若调用了prestartAllCoreThreads()或者 prestartCoreThread() 方法,预创建线程,在任务没来之前就创建corePoolSize个线程或者一个线程)
-
maximumPoolSize:线程池最大线程数量
-
keepAliveTime:存活时间。如果当前线程池中的线程数量比核心线程数量要多,并且是闲置状态的话,这些闲置的线程能存活的最大时间(如果调用了allowCoreThreadTimeOut(boolean)方法,线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,知道线程池中的线程数为0)
-
unit:存活时间的单位,有7中取值,在TimeUnit类中有7中静态属性
-
workQueue:等待队列,用于保存等待执行的任务的阻塞队列,当任务提交时,如果线程池中的线程数量大于或者等于corePoolSize时,把该任务封装成一个worker对象,放入等待队列中(如基于数组的有界ArrayBlockingQueue 按FIFO排序任务;基于链表的无界LinkedBlokingQueue 按FIFO排序任务,吞吐量通常高于前者;一个不存储元素的阻塞队列SynchronousQuene 每个插入操作必须等到另一个线程调用移除操作,斗则插入操作会一直处于阻塞状态,吞吐量通常高于前者;具有优先级的无界阻塞队列PriorityBlockingQueue等)
-
ThreadFactory:创建线程的工厂,可以自定义工厂,控制创建的线程名称来辅助排查问题(默认使用Executors.defaultThreadFactory() 来创建线程,使用默认的ThreadFactory来创建线程时,会使新创建的线程具有相同的priority即优先级,并且是非守护线程,用时也设置了线程的名称)
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
- Handler:线程池的饱和策略,当队列满了且线程个数达到maximunPoolSize后采取的策略,
四种饱和策略
1. AbortPolicy
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* 直接抛出异常,这是默认策略
*/
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
2. CallerRunsPolicy
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* 用调用者所在的线程来执行任务
*/
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
3. DiscardOldestPolicy
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* 丢弃阻塞队列中靠最前的任务,并执行当前任务;
*/
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
4. DiscardPolicy
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* 丢弃该任务
*/
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
5. 实现RejectExecutionHandler接口的自定义handler
工作线程-worker
|
woker实现了Runnable接口,同时继承了AQS类,并持有一个线程thread,一个初始化任务:firstTask。负责处理任务,同时维护线程状态thread一般是在调用构造方法时通过ThreadFactory来创建的线程,可以用来执行任务fitstTask是传入的第一个任务,如果非空,那么线程在启动初期立即执行这个任务,也就对应核心线程创建的情况;如果这个值是Null,那么就需要去执行workQueue中的任务。单个任务执行完毕后,worker会继续在workQueue中获取下一个任务继续执行 |
---|
原理
线程池内部是一个生产者-消费者模型,阻塞队列workQueue存储所有的任务,而workers存储所有的工作线程,线程池内部主要通过维护这两个属性维持线程池状态
每次提交任务都会根据不同情况,执行不同的策略:1.创建worker直接执行;2,缓存到队列;3.拒绝执行。当工作线程执行完队列中所有任务时,线程就会被回收
ThreadPoolExecutor内部使用AtomicInterger类型的变量 ctl 来维护线程池的运行状态(runState)和线程池中的有效线程数量(workerCount),即高三位为状态位,其余表示线程数目
RUNNING(-1: 111) | 可以接受新的任务,也可以处理阻塞队列里的任务 |
---|---|
SHUTDOWN(0:000) | 不接受接的任务,但是可以处理阻塞队列里的任务 |
STOP(1:001) | 不接受新的任务,不处理阻塞队列里的任务,中断正在处理的任务 |
TIDYING(2:010) | 过度状态,也就是说所有的任务都执行完了,当前线程池已经没有有效的线程,这个时候线程池的状态将会TIDYING,并且将要调用terminated方法 |
TERMINATED(3:011) | 终止状态。terminated方法调用完成以后的状态 |
任务的执行:execute -> addWorker -> runworker(getTask)
线程池的工作线程通过Woker类实现,在ReentrantLock锁的保证下,把Woker实例插入到HashSet后,并启动Woker中的线程。 从Woker类的构造方法实现可以发现: 线程工厂在创建线程thread时,将Woker实例本身this作为参数传入,当执行start方法启动线程thread时,本质是执行了Worker的runWorker方法。 firstTask执行完成之后,通过getTask方法从阻塞队列中获取等待的任务,如果队列中没有任务,getTask方法会被阻塞并挂起,不会占用cpu资源;
execute
/**
* execute是线程池整体调度的方法,是线程池暴露对外执行任务的入口,
* 对添加的任务根据不同场景主要有三种执行逻辑
**/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//1.当前正在运行线程,若小于核心线程数,创建新的核心线程
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//2.poolSize >= corePoolSize 校验线程状态,正常则将任务添加到阻塞队列中
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//第二次校验,防止在第一次校验通过后线程池关闭。如果线程池关闭,在队列中删除任务,执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
//如果线程数=0,新增一个非核心的线程(poolSiza> corePoolSize)
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//3.线程队列已满,尝试创建新非核心线程执行任务,失败执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
addworker
/**
* addworker主要是向线程中添加新线程
* 新线程主要有两种:一种是核心线程,另一种是非核心线程,通过实际情况区分
* 由于工作线程列表workers是hashSet类型,因此添加线程的时候需要注意线程安全,
* 添加完毕后同时调用worker.thread.start方法启动任务
**/
//全局锁
private final ReentrantLock mainLock = new ReentrantLock();
private boolean addWorker(Runnable firstTask, boolean core) {
//CAS更新线程池数量
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 {
//创建worker
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
// 线程池重入锁
final ReentrantLock mainLock = this.mainLock;
//加独占锁,为了workers同步,因为可能多个线程调用了线程池的execute方法
mainLock.lock();
try {
//重新检查线程池状态,为了避免 在获取锁前调用了shutdown接口
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;
runworker
/**
* runWorker是每个工作线程执行的地方
* 先执行第一个任务(如果有),再从任务队列中取任务来执行
* 这里留了两个钩子方法beforeExecute和afterExecute方便开发者自定义扩展添加逻辑
**/
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//getTask()之后执行任务才会对worker加锁,如果线程调用了shutdown,获取任务会有影响,但是执行不会中断
//先执行firstTask ,再从workerQueue中取task
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);
}
}
getTask
/**
*getTask是从阻塞队列获取等待的任务,如果队列中没有任务,getTask方法会被阻塞并挂起,不会占用cpu资源
**/
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// rs》=SHUTDOWN 且 是stop状态 || work为空 工作线程-1,返回null.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// 核心线程是否允许超时 || 此时已经创建非核心线程
//新版线程池允许核心线程超时,调用allowCoreThreadTimeOut()方法,若为true,则核心线程超时则会回收线程
//allowCoreThreadTimeOut为false,线程即使空闲也不会被销毁;倘若为ture,在keepAliveTime内仍空闲则会被销毁
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//(当线程大于最大线程(setMaximumPoolSize可调整) || 超时控制&&上一轮拉取失败)
//工作线程总数大于1或者任务队列为空,则通过CAS把线程数减1,同时返回null
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//timed为true(线程不允许无休止空闲),通过poll()方法做超时拉取,keepAliveTime时间内没有等待到有效的任务,则返回Null
//timed为false(线程允许空闲等待而不是销毁),通过take()做阻塞拉取,如果阻塞队列为空,当前线程会被挂起等待;
//当队列中有任务加入时,线程被唤醒,take方法返回任务,并执行
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
//中断标记是在调用shutdown()或者shutdownNow()的时候设置进去的
timedOut = false;
}
}
任务的提交
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JhRrgpfr-1648628903386)(/Users/zhaojiahui20/Documents/备忘录/个人总结/线程任务提交.png)]
例子
/**
*1. submit任务,等待线程池execute
*2. 执行FutureTask类的get方法时,会把主线程封装成WaitNode节点并保存在waiters链表中,并阻塞等待运行结果
*3. FutureTask任务执行完成后,通过UNSAFE设置waiters响应的waitNode为null,并通过LockSupport类unpark方法唤醒主线程
**/
public class Test{
public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool();
Future<String> future = es.submit(new Callable<String>() {
@Override
public String call() throws Exception {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "future result";
}
});
try {
String result = future.get();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
submit方法
AbstractExecutorService.submit()实现了ExecutorService.submit() 可以获取执行完的返回值, 而ThreadPoolExecutor 是AbstractExecutorService.submit()的子类,所以submit方法也是ThreadPoolExecutor`的方法
// submit()在ExecutorService中的定义
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
// submit方法在AbstractExecutorService中的实现
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
// 通过submit方法提交的Callable任务会被封装成了一个FutureTask对象。
RunnableFuture<T> ftask = newTaskFor(task);
//通过Executor.execute方法提交FutureTask到线程池中等待执行,最终执行的是RutureTask的run方法
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
FutureTask对象
public class FutureTask<V> implements RunnableFuture<V>{
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
//get方法
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
//内部通过awaitDone方法对主线程进行阻塞
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
//如果主线程被中断,则排除中断异常
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
//判断FutureTask当前的state,如果大于COMPLETING,说明任务已经执行完成,则直接返回
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
//等于COMPLETING,说明任务已执行完,这时主线程只需通过yield方法让出cpu资源,等待state变成NORMAL
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
//通过waitNode类封装当前线程,并通过UNSAFE添加到waiters链表
else if (q == null)
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
//最终通过LockSupport的park货parkNanos挂起线程
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
}
run方法
//FutureTask.run方法是在线程池中被执行的,而非主线程
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//通过执行Callable任务的call方法
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
//如果call执行成功,则通过set方法保存结果,否则setException保存异常
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
任务的关闭
//shutdown方法会将线程池的状态设置为SHUTDOWN,线程池进入这个状态后,就拒绝再接受任务,然后会将剩余的任务执行完
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
//重入锁
mainLock.lock();
try {
//检测是否可以关闭线程
checkShutdownAccess();
//设置线程池状态
advanceRunState(SHUTDOWN);
//尝试中断worker
interruptIdleWorkers();
//预留方法,留给子类实现
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//遍历所有worker
for (Worker w : workers) {
Thread t = w.thread;
//先尝试调用w.tryLock(),如果获取到锁,就说明worker是空闲的,就可以直接中断它
//worker自己本身实现了AQS同步框架,然后实现的类似锁的功能
//它实现的锁是不可重入的,所有如果worker在执行任务的时候,会先进行加锁,这里tryLock()就会返回false
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
//shutdownNow方法,先将线程池状态设置为STOP,然后拒绝所有提交的任务。最后中断左右正在运行中的worker,然后情况任务队列
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
//检验权限
advanceRunState(STOP);
//中断所有的worker
interruptWorkers();
//情况任务队列
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//遍历所有worker,然后调用中断方法
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
线程池大小如何选定
性质不同的任务可使用不同规模的线程池分开处理:
CPU密集型: 尽可能少的线程 线程数 = 按照核数或者(核数+1)设定
IO密集型:尽可能多的线程 线程数 = CPU数*(1+平均等待时间/平均工作时间)比如数据库连接池
混合型:CPU密集型的任务与IO密集型任务的执行时间差别较小,拆分为两个线程池;否则没必要拆分