11、线程池(重点)
线程池有三大方法、七大参数、4中拒绝策略
什么是池化技术
简单理解:预先创建,要用就拿
池化"(Pooling)是一种资源管理技术,用于优化资源的使用效率,减少资源创建和销毁的开销。池化技术的核心思想是预先创建并维护一组资源对象(如线程、数据库连接、对象实例等),当需要使用这些资源时,从池中获取一个可用的资源,使用完毕后将其归还到池中,而不是每次都重新创建和销毁资源。
这里讲述池化技术之一>线程池
线程池的好处:
- 减少线程创建和销毁的开销:线程的创建和销毁是相对耗时的操作,线程池通过复用线程,避免了频繁的线程创建和销毁,从而提高了程序的性能。
- 提高线程的可管理性:线程池可以对线程进行统一管理,例如限制线程的最大数量、设置线程的优先级等,避免线程过多导致的系统资源耗尽问题。
- 提高响应速度:当有任务到来时,可以直接从线程池中获取线程执行,而不需要等待线程的创建,从而提高了系统的响应速度。
下面用自定义线程池的代码演示三大方法,七大参数、四大拒绝策略
java
package com.yw.Threadchi;
import java.util.concurrent.*;
public class Demo1 {
public static void main(String[] args) {
//三大线程池操作,不建议,可能会造成资源损耗
// ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();//单个线程
// ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);//创建一个固定大小的线程池
// ExecutorService cachedThreadPool = Executors.newCachedThreadPool();//可伸缩的线程池,可根据情况调节自身大小
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
try {
for (int i = 0;i < 9;i++) {
threadPoolExecutor.execute(() -> {
System.out.println(Thread.currentThread().getName() + "OK~");
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
threadPoolExecutor.shutdown();
}
}
}
corePoolSize
含义:核心线程数,即线程池中始终保持的线程数量。
作用:
- 当线程池中的线程数量小于
corePoolSize
时,即使有线程空闲,线程池也会优先创建新的线程来处理任务。 - 这些核心线程默认会一直存活,即使它们处于空闲状态,也不会被销毁(除非设置了
allowCoreThreadTimeOut
为true
)。
maximumPoolSize
含义:最大线程数,即线程池中允许的最大线程数量。
作用:
- 当线程池中的线程数量达到
corePoolSize
且任务队列已满时,线程池会尝试创建新的线程来处理任务,但线程数量不会超过maximumPoolSize
。 - 如果线程池中的线程数量已经达到
maximumPoolSize
,且任务队列已满,此时再有新任务提交,线程池会拒绝执行任务,并抛出RejectedExecutionException
异常(除非设置了拒绝策略)。
keepAliveTime
含义 :非核心线程的空闲存活时间,即当线程池中的线程数量大于 corePoolSize
时,多余的线程在空闲状态下可以存活的最大时间。
作用:
- 如果一个线程在空闲状态下超过了
keepAliveTime
,它会被销毁,从而减少线程池的资源占用。 - 该参数仅对非核心线程有效,核心线程默认不会被销毁(除非设置了
allowCoreThreadTimeOut
为true
)。
unit
含义 :keepAliveTime
参数的时间单位,用于指定 keepAliveTime
的时间单位。
常见的时间单位有 TimeUnit.SECONDS
(秒)、TimeUnit.MILLISECONDS
(毫秒)、TimeUnit.MINUTES
(分钟)等。
workQueue
含义:任务队列,用于存储等待执行的任务。
作用:
- 当线程池中的线程数量达到
corePoolSize
时,新提交的任务会被放入任务队列中等待执行。 - 如果任务队列已满且线程池中的线程数量小于
maximumPoolSize
,线程池会创建新的线程来处理任务。 - 如果大于了
maximumPoolSize,会根据对应的拒绝策略拒绝线程
常见任务队列类型:
ArrayBlockingQueue
:基于数组的有界阻塞队列,需要指定队列容量。LinkedBlockingQueue
:基于链表的无界阻塞队列(实际上有最大容量限制,但通常很大)。SynchronousQueue
:不存储任务的阻塞队列,每个插入操作必须等待一个移除操作,反之亦然。PriorityBlockingQueue
:支持优先级排序的无界阻塞队列。
threadFactory
含义:线程工厂,用于创建新线程。
作用:
-
默认情况下,线程池会使用
Executors.defaultThreadFactory()
创建线程,但可以通过自定义ThreadFactory
来设置线程的名称、优先级等属性。 -
一般不会改变,直接用Executors.defaultThreadFactory();
handler
含义:拒绝策略,用于处理线程池拒绝任务的情况。
作用:
- 当线程池中的线程数量达到
maximumPoolSize
且任务队列已满时,再有新任务提交,线程池会调用拒绝策略来处理这些任务。
四大拒绝策略
-
AbortPolicy
:默认拒绝策略,抛出RejectedExecutionException
异常。 -
CallerRunsPolicy
:由提交任务的线程(调用者线程)来执行任务。 -
DiscardPolicy
:直接丢弃任务,不抛出异常。 -
DiscardOldestPolicy
:丢弃队列中最老的任务(即队列头部的任务),然后尝试将新任务加入队列。
扩展:最大线程该如何定义?
根据具体任务类型cpu密集型和io密集型来确定最大线程,是优化线程池性能的关键步骤
CPU密集型任务
对于CPU密集型任务,线程池中的线程主要在执行计算操作,CPU利用率较高。因此,线程数量应与CPU核心数相匹配,以避免过多的上下文切换和资源竞争。
建议的最大线程数
-
核心线程数(
corePoolSize
):设置为CPU核心数。 -
最大线程数(
maximumPoolSize
):设置为CPU核心数的1.5到2倍。
I/O密集型任务
对于I/O密集型任务,线程大部分时间都在等待I/O操作完成,CPU利用率较低。因此,可以设置更多的线程来提高系统的并发性能。
建议的最大线程数
-
核心线程数(
corePoolSize
):设置为CPU核心数的2到4倍。 -
最大线程数(
maximumPoolSize
):设置为CPU核心数的数倍,甚至更高(如10倍)。