主要讨论以下问题:
- 认识Java线程池
- 线程池的种类,区别,和使用场景
- 线程池的工作流程
- 线程池几个参数的理解
- 分析线程池的实现原理
- 线程池中 工作线程的调度过程。
- 线程池如何调优,线程池的最大线程数目根据什么确定
什么是线程池
java.util.cuncurrent.Executors
提供了一个 java.util.concurrent.Executor
的接口实现,用于创建线程池。
多线程记住主要解决处理器单元内多个线程执行的问题,它可以显著减少CPU的闲置时间,增加CPU的吞吐量。
一个线程池包括以下四个基本部分:
1)线程池管理器(ThreadPool):用于创建并管理线程池,包括,创建线程池,销毁线程池,添加新任务。
2)工作线程(PookWorker):线程池的中的线程,在没有任务提交的时候处于等待状态,可以循环的执行任务。
3)任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完成以后的收尾工作,以及任务的执行状态。
4)任务队列(TaskQueue):存放待处理的任务,提供一种缓冲机制。
线程池技术的关键是缩短线程的创建和销毁所消耗的时间,复用已经创建的线程,减少创建的线程数,从而提高服务性能。
线程池的种类

在 java.util.cuncurrent.Executors
下,Java提供了四种基本的线程池(还有其他的暂时不看了):
1)newFixedThreadPool:固定线程数量的线程池(核心线程数=最大线程数),提交任务到线程最大数量后,就把任务放到无界队列中(LinkedBlockingQueue)。
工作队列大小无限制,但是可能造成工作队列提交任务过多,资源耗尽。
源码如下:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
// 自定义ThreadFactory
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
2)newCachedThreadPool:可缓存线程池(核心线程数=0,最大线程数=Integer.MAX_VALUE)。可以回收空闲线程(默认设定60秒过期时间),当有任务提交时,启动新线程执行。工作队列使用同步队列(SynchronousQueue)。
最大线程数无限制,当提交任务过多时,可能造成创建大量线程,导致资源耗尽。
源码如下:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
// 自定义ThreadFactory
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
3)newSingleThreadExecutor:单线程化的线程池,只会用唯一的工作线程来执行任务,保证所有被提交的任务按照FIFO的顺序执行。
工作队列是一个阻塞队列(LinkedBlockingQueue)
源码如下:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
// 自定义ThreadFactory
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
4)newScheduledThreadPool:大小无限制的线程池,支持定时和周期性的执行线程。由ScheduledThreadPoolExecutor创建(其又继承自ThreadPoolExecutor)。
源码如下:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
// 自定义ThreadFactory
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
// 单线程化的ScheduledThreadPool
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
// 单线程化的ScheduledThreadPool 自定义ThreadFactory
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1, threadFactory));
}
5)newWorkStealingPool:返回拥有多个任务队列的线程池,结合JDK1.7中引入的 Fork/Join 框架来理解。(TODO)
源码如下:
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
线程池的参数
主要参数如下:
corePoolSize:int型,核心线程数
maximumPoolSize:int型,最大线程数
keepAliveTime:long型,线程的存活时间(默认非核心线程空闲时会被回收)
unit:TimeUnit型,线程存活时间单位
BlockingQueue:工作队列
ThreadFactory:自定义工作线程工厂
RejectExecutionHandler:拒绝策略Handler
线程存活时间和时间单位:如果线程数大于核心线程,那么空闲的线程会在存活一定时间之后被回收。直到活跃线程数等于核心线程数。
也可以通过设置 allowCoreThreadTimeOut(boolean value)
来指定允许核心线程被回收。
工作队列:
工作队列是一个 BlockingQueue,只要是实现了阻塞队列的都可以。具体可以包括 ArrayBlockingQueue
,LinkedBlockingQueue
,SynchronousQueue
,DelayQueue
等等。不同的工作队列有不同的运行特性。
拒绝策略:
拒绝策略 RejectedExecutionHandler
是一个接口,主要作用是,当线程池拒绝任务之后,执行该接口定义的方法来执行拒绝以后的行为。
可以自定义行为,当然,Java也提供了几种策略供选择:
1)new ThreadPoolExecutor.CallerRunsPolicy()
重试执行当前任务,但会交由调用者线程来执行该任务。
2)new ThreadPoolExecutor.AbortPolicy()
抛出RejectedExecutionException
异常
3)new ThreadPoolExecutor.DiscardPolicy()
不做任何动作
4)new ThreadPoolExecutor.DiscardOldestPolicy()
抛弃线程池中最后要执行的那个任务,并执行新传入的任务。
线程池的工作流程

PS:具体流程可以参考源码:
java.util.concurrent.ThreadPoolExecutor.execute(Runnable command)
主要分为四个步骤:
1)如果线程池中的线程数小于核心线程数,则会尝试创建一个新线程,该任务被创建的新线程立即执行。
2)如果线程数已经达到了核心线程数,但是工作队列没有满,则该任务会尝试加入到工作队列中。
3)如果工作队列也满了,那么会尝试创建新线程,直到达到了最大线程数。
4)如果达到了最大线程数,那么又有新任务提交进来时,则会抛出异常或者执行拒绝策略。
向线程池中提交任务
主要有两种方式:
1)execute:由Executor 接口定义,接受Runnable的任务,返回void。ExecutorService接口继承该接口,然后由ThreadPoolExecutor实现。
简单来说,就是不需要获取执行结果的,使用该方法。
2)submit:由ExecutorService 接口定义,有三个重载的方法,分别支持接受Runnable和Callable的任务。
在 AbstractExecutorService
的实现中,接收task,包装为一个RunnableFuture类型,并交由execute执行,并返回该RunnableFuture对象。
具体重载方法中,接受Runnable的重载方法会返回null。接受Callable参数的会把执行结果包装为 Future 对象返回。
submit 的主要优势在于,方便Exception处理。如果希望捕获task中出现的异常并作出相应处理的话,可以通过Future.get() 抛出的异常,来进行相应的处理。这一点 execute 是无法做到的,也是使用 submit 最主要的作用。
至于 Future 以及 FutureTask,会单独开一篇用于复习。
线程池的实现原理
(如果有什么错误或者建议,欢迎留言指出)
(本文内容是对各个知识点的转载整理,用于个人技术沉淀,以及大家学习交流用)
参考资料:
Java-线程池专题 (美团面试题)
Java线程池原理详解
Java中线程池——ThreadPoolExecutor原理
Java多线程——ThreadPooExecutor详解
Java线程池种类和使用场景
Java线程池调优
Java中线程技术及其优化
Java并发编程——线程池的使用
线程池submit与execute的区别
网友评论