Java线程池
参考博文:
https://blog.****.net/javazejian/article/details/50890554
1. Executor框架浅析
1.1 为什么要使用线程池
在Java中,线程的创建和销毁需要一定的开销,如果我们为每一个任务创建一个新的线程来执行的话,那么这些线程的创建与销毁将消耗大量的计算资源。同时为每一个任务创建一个新线程来执行,这样的方式可能会使处于高负荷状态的应用最终崩溃
因此,我们在线程池中创建若干条线程,当有任务需要执行时就从该线程池中获取一条线程来执行任务,如果一时间任务过多,超出线程池的线程数量,那么后面的线程任务就进入一个等待队列进行等待,直到线程池有线程处于空闲时才从等待队列获取要执行的任务进行处理. 1.2Executor框架的两级调度模型 用户的线程到操作系统的线程的映射方式是这样实现的,在上层,java多线程程序通过把应用分为若干个任务,然后使用用户级的调度器(Executor框架)将这些任务映射为固定数量的线程;在底层,操作系统内核将这些线程映射到硬件处理器上。这样种两级调度模型如下图所示:
1.3 Executor框架的结构
Executor框架的结构主要包括3个部分
-
任务:包括被执行任务需要实现的接口:Runnable接口或Callable接口
-
任务的执行:包括任务执行机制的核心接口Executor,以及继承自Executor的EexcutorService接口。Exrcutor有两个关键类实现了ExecutorService接口(ThreadPoolExecutor和ScheduledThreadPoolExecutor)。
(Java中类只能继承类且只能单继承,接口只能继承接口可以多继承)
- 异步计算的结果:包括接口Future和实现Future接口的FutureTask类
-
Extecutor是一个接口,它是Executor框架的基础,它将任务的提交与任务的执行分离开来。
-
ThreadPoolExecutor是线程池的核心实现类,用来执行被提交的任务。
ScheduledThreadPoolExecutor是一个实现类,可以在给定的延迟后运行命令,或者定期执行命令。 -
ScheduledThreadPoolExecutor比Timer更灵活,功能更强大。
Future接口和实现Future接口的FutureTask类,代表异步计算的结果。 -
Runnable接口和Callable接口的实现类,都可以被ThreadPoolExecutor或者ScheduledThreadPoolExecutor执行。区别就是Runnable无法返回执行结果,而Callable可以返回执行结果。
-
主线程创建实现Runnable或Callable接口的任务对象
-
将Runnable转成Callable对象,然后可以把Runnable对象直接提交给 ExecutorService执行,方法为ExecutorService.execute(Runnable
command); -
把Runnable对象或者Callable对象提交给ExecutorService执行,方法为 ExecutorService.submit(Runnable
task)或ExecutorService.submit(Callable task)。(返回一个futrueTask)
1.4ThreadPoolExecutor浅析
1.4.1构造方法
public ThreadPoolExecutor(
int corePoolSize,//线程池的核心线程数(默认下,核心线程一直存活)
int maximumPoolSize,//最大线程数(若超出,那么拒绝执行)
long keepAliveTime,//非核心线程的超时时长
TimeUnit unit,//指定时间的单位
BlockingQueue<Runnable> workQueue,//线程池中的任务队列
ThreadFactory public ThreadPoolExecutor//线程工厂
)
1.4.2. 运行流程
-
如果线程池的数量还未达到核心线程的数量,那么会直接启动一个核心线程来执行任务
-
如果线程池中的线程数量已经达到或者超出核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
-
如果在步骤(2)中无法将任务插入到任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行务。
-
如果在步骤(3)中线程数量已经达到线程池规定的最大值,那么就会拒绝执行此任务
1.4.3三种常见的线程池
1.4.3.1 FixedThreadPool(没有非核心线程,使用无界队列)
FixedThreadPool模式会使用一个固定数目的核心线程来处理若干数目的任务。规定数目的线程处理所有任务,一旦有线程处理完了任务就会被用来处理新的任务(如果有的话)
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(
nThreads, //核心线程数
nThreads,//最大线程数
0L,//非核心线程数生存时间
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
运行流程:
- 如果当前运行线程数<核心线程数,创建一个新的线程来执行任务.
- 当前线程池中的线程数量达到核心线程数时,新的任务将进入无界队列等待.
- 由于使用无界队列,运行中的FixedThreadPool不会拒绝任务
- 线程在执行任务后,会在循环中反复从LinkedBlockingQueue获取任务来执行
3.2 CachedThreadPool
创建足够多的线程来执行任务(Task)。随着程序执行的过程,有的线程执行完了任务,可以被重新循环使用时,才不再创建新的线程来执行任务
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor
(0, //核心线程数为0
Integer.MAX_VALUE,//最大线程数为int最大值
60L,
TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
运行流程:
1.新的任务提交至队列中,此时,如果线程池中存在空闲线程,那么配对成功,由空闲线程来执行该任务.
2.若没有空闲线程时,线程池则会创建一个新的线程来执行该任务
3.执行完任务后,线程开始空转,若空转实践超过了60S那么则终止该线程
3.3 SingleThreadPool(可看作是只有一个核心线程的FixedThreadPool)
SingleThreadExecutor模式只会创建一个线程。它和FixedThreadPool比较类似,不过线程数是一个。如果多个任务被提交给SingleThreadExecutor的话,那么这些任务会被保存在一个队列中,并且会按照任务提交的顺序,一个先执行完成再执行另外一个线程。SingleThreadExecutor模式可以保证只有一个任务会被执行。这种特点可以被用来处理共享资源的问题而不需要考虑同步的问题。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
3.4.各自使用的场景
-
FixedThreadPool:适用于负载比较重的服务器
-
CachedThreadPool: 用于执行很多的短期异步任务的小程序,或者负载较轻的服务器
-
SingleThreadPool: 适用于需要保证执行顺序地执行各个任务;并且在任意时间点,不会有多个线程是活动的场景
2. ScheduledThreadPoolExecutor浅析
2.1 ScheduledThreadPoolExecutor执行机制分析
ScheduledThreadPoolExecutor继承自ThreadPoolExecutor。它主要用来在给定的延迟之后执行任务,或者定期执行任务。ScheduledThreadPoolExecutor的功能与Timer类似,但比Timer更强大,更灵活,Timer对应的是单个后台线程,ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数
2.1.1 创建ScheduledThreadPoolExecutor
2.1.1.1 ScheduledThreadPoolExecutor执行并行任务,支持多线程
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
底层源码:
public ScheduledThreadPoolExecutor(int corePoolSize) {
//类似于FixedThreadPool,只有核心线程没有额外线程
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
2.1.1.2 SingleThreadScheduledExecutor执行单线程
public static ScheduledExecutorService newSingleThreadScheduledExecutor()
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
2.1.2 使用ScheduledThreadPoolExecutor
-
Schedule方法
schedule(Runnable command,long delay, TimeUnit unit)
//command:实现了Runnable的任务
//delay:任务开始执行的延时
//unit:时间单位 -
scheduleWithFixedDelay方法(作用是预定在初始的延迟结束后,周期性地执行给定的任务)
public ScheduledFuture<?> scheduleWithFixedDelay
(Runnable command,long initialDelay,long delay,TimeUnit unit);
//initialDelay:初始延迟
//delay:周期延迟
比如10s开始执行任务,然后每一秒开始一个新任务,那么initialDelay=10,delay=1