|
|
线程的创建与销毁需要依赖操作系统,其代价是比较高昂的,频繁地创建与销毁线程对系统性能影响较大。
|
|
|
出于线程管理的需要,线程池应运而生。线程池是一种多线程处理形式,处理过程中将任务提交到线程池,任务的执行交由线程池来管理。使用线程池的好处在于:
|
|
|
- 降低资源消耗:线程池通常会维护一些线程(数量为 corePoolSize),这些线程被重复使用来执行不同的任务,任务完成后不会销毁。在待处理任务量很大的时候,通过对线程资源的复用,避免了线程的频繁创建与销毁,从而降低了系统资源消耗。
|
|
|
- 提高响应速度:由于线程池维护了一批 alive 状态的线程,当任务到达时,不需要再创建线程,而是直接由这些线程去执行任务,从而减少了任务的等待时间。
|
|
|
- 提高线程的可管理性:使用线程池可以对线程进行统一的分配,调优和监控。
|
|
|
|
|
|
# 一、java中提供的线程池
|
|
|
|
|
|
## 1.1 Executor
|
|
|
在Java中,线程池是由Executor框架实现的,Executor是最顶层的接口定义,其子类和实现类包括:**ExecutorService,ScheduledExecutorService,ThreadPoolExecutor,ScheduledThreadPoolExecutor,ForkJoinPool**等。
|
|
|
1. **Executor**:Executor是一个接口,只定义了一个execute()方法(void execute(Runnable command);),只能提交Runnable形式的任务,不支持提交Callable带有返回值的任务。
|
|
|
2. **ExecutorService**:ExecutorService在Executor的基础上加入了线程池的生命周期管理,可以通过shutdown或者shutdownNow方法来关闭线程池,关于这两个方法后文有详细说明。ExecutorService支持提交Callable形式的任务,提交完Callable任务后拿到一个Future(代表一个异步任务执行的结果)。
|
|
|
3. **ThreadPoolExecutor**:是线程池中最核心的类,后面有详细说明。
|
|
|
4. **ScheduledThreadPoolExecutor**:ThreadPoolExecutor子类,它在ThreadPoolExecutor基础上加入了任务定时执行的功能。
|
|
|
|
|
|
|
|
|
## 1.2 线程池的创建
|
|
|
Executors中提供了一系列静态方法创建线程池:
|
|
|
- **newSingleThreadExecutor**:一个单线程的线程池。如果因异常结束,会再创建一个新的,保证按照提交顺序执行。
|
|
|
- **newFixedThreadPool**:创建固定大小的线程池。根据提交的任务逐个增加线程,直到最大值保持不变。如果因异常结束,会新创建一个线程补充。 newCachedThreadPool:创建一个可缓存的线程池。会根据任务自动新增或回收线程。
|
|
|
- **newScheduledThreadPool**:支持定时以及周期性执行任务的需求。
|
|
|
- **newWorkStealingPool**:JDK8新增,根据所需的并行层次来动态创建和关闭线程,通过使用多个队列减少竞争,底层使用ForkJoinPool来实现。优势在于可以充分利用多CPU,把一个任务拆分成多个“小任务”,放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。
|
|
|
- newCachedThreadPool:缓存线程池
|
|
|
|
|
|
![[Snipaste_2023-02-28_11-48-27.png]]
|
|
|
|
|
|
|
|
|
如下图所示,这些线程池都大部分是由ThreadPoolExecutor构造而成
|
|
|
![[Snipaste_2023-02-28_11-48-27 1.png]]
|
|
|
|
|
|
## 1.3 ThreadPoolExecutor
|
|
|
![[Snipaste_2023-02-28_13-09-43.png]]
|
|
|
|
|
|
### corePoolSize
|
|
|
线程池容量(初始化线程数),这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法(从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程)。当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
|
|
|
|
|
|
### maximumPoolSize
|
|
|
线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程。
|
|
|
|
|
|
### keepAliveTime
|
|
|
表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0。
|
|
|
|
|
|
### unit
|
|
|
参数keepAliveTime的时间单位
|
|
|
|
|
|
### workQueue
|
|
|
工作队列,一般选择有界队列防止内存溢出
|
|
|
1. **ArrayBlockingQueue**:基于数组结构的有界阻塞队列,按FIFO排序任务;
|
|
|
2. **LinkedBlockingQuene**:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
|
|
|
3. **SynchronousQuene**:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
|
|
|
4. **priorityBlockingQuene**:具有优先级的无界阻塞队列;
|
|
|
|
|
|
### threadFactory
|
|
|
|
|
|
### handler
|
|
|
表示当拒绝处理任务时的策略
|
|
|
1. **ThreadPoolExecutor.AbortPolicy**:丢弃任务并抛出RejectedExecutionException异常。
|
|
|
2. **ThreadPoolExecutor.DiscardPolicy**:也是丢弃任务,但是不抛出异常。
|
|
|
3. **ThreadPoolExecutor.DiscardOldestPolicy**:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
|
|
|
4. **ThreadPoolExecutor.CallerRunsPolicy**:由调用线程处理该任务
|
|
|
|
|
|
|
|
|
## 1.4 线程池的关闭
|
|
|
ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow(),其中:
|
|
|
- **shutdown()**:不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
|
|
|
- **shutdownNow()**:立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务
|
|
|
|
|
|
## 1.5 线程池容量的动态调整
|
|
|
ThreadPoolExecutor提供了动态调整线程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(),
|
|
|
- **setCorePoolSize**:设置核心池大小
|
|
|
- **setMaximumPoolSize**:设置线程池最大能创建的线程数目大小
|
|
|
当上述参数从小变大时,ThreadPoolExecutor进行线程赋值,还可能立即创建新的线程来执行任务。 |