🌈🌈🌈🌈🌈🌈🌈🌈
添加微信好友(通过文章导读添加)一起交流学习,也可以找我拿一下整篇文章PDF!
【11来了】文章导读地址:点击查看文章导读!
🍁🍁🍁🍁🍁🍁🍁🍁
线程池到底怎么运行的?
简单介绍一下线程池
首先,线程池是将多个线程进行池化操作,统一进行管理,这样做有什么好处呢?
降低创建、销毁线程的开销:线程池中维护固定数量的线程,不需要临时进行线程的创建和销毁提高响应速度:对于新提交到线程池中的任务,直接使用线程池中的空闲线程可以直接进行处理,不需要等待创建线程节省资源:可以重复利用线程
线程池中重要的参数
线程池中重要的参数如下:
-
corePoolSize:核心线程数量 -
maximumPoolSize:线程池最大线程数量 = 非核心线程数+核心线程数 -
keepAliveTime:非核心线程存活时间 -
unit:空闲线程存活时间单位(keepAliveTime单位) -
workQueue:工作队列(任务队列),存放等待执行的任务- LinkedBlockingQueue:无界的阻塞队列,最大长度为 Integer.MAX_VALUE
- ArrayBlockingQueue:基于数组的有界阻塞队列,按FIFO排序
- SynchronousQueue:同步队列,不存储元素,对于提交的任务,如果有空闲线程,则使用空闲线程来处理;否则新建一个线程来处理任务
- PriorityBlockingQueue:具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
-
threadFactory:线程工厂,创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。 -
handler: 拒绝策略 ,有4种- AbortPolicy :直接抛出异常,默认策略
- CallerRunsPolicy:用调用者所在的线程来执行任务
- DiscardOldestPolicy:丢弃阻塞队列里最老的任务,也就是队列里靠前的任务
- DiscardPolicy :当前任务直接丢弃
新加入一个任务,线程池如何进行处理呢?
新加入一个任务,线程池处理流程如下:
- 如果核心线程数量未达到,创建核心线程执行
- 如果当前运行线程数量已经达到核心线程数量,查看任务队列是否已满
- 如果任务队列未满,将任务放到任务队列
- 如果任务队列已满,看最大线程数是否达到,如果未达到,就新建非核心线程处理
- 如果当前运行线程数量未达到最大线程数,则创建非核心线程执行
- 如果当前运行线程数量达到最大线程数,根据拒绝策略处理
如何将任务提交到线程池中呢?
有两种方式:execute 和 submit
这两种方式的区别:
-
execute
- execute 没有返回值
- execute 无法捕获任务过程中的异常
-
submit
- submit 会返回一个
Future对象,用来获取任务的执行结果 - submit 可以通过 Future 对象来捕获任务中的异常
- submit 会返回一个
execute 方式如下:
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.execute(new Runnable() {
public void run() {
// 执行具体的任务逻辑
System.out.println("Task executed using execute method");
}
});
executor.shutdown();
submit 方式如下:
ExecutorService executor = Executors.newFixedThreadPool(5);
Future<String> future = executor.submit(new Callable<String>() {
public String call() {
// 执行具体的任务逻辑
return "Task executed using submit method";
}
});
try {
String result = future.get(); // 获取任务执行结果
System.out.println(result);
} catch (InterruptedException e) {
// 处理中断异常
} catch (ExecutionException e) {
// 处理任务执行异常
} finally {
// 关闭线程池
executor.shutdown();
}
线程池是如何关闭的呢?
通过调用线程池的 shutdown() 方法即可关闭线程池
调用之后,会设置一个标志位表示当前线程池已经关闭,会禁止向线程池中提交新的任务
去中断所有的空闲线程并且等待正在执行的任务执行完毕(通过调用线程 interrupt() 方法),当线程池中所有任务都执行完毕之后,线程池就会被完全关闭
扩展:thread.interrupt() 方法调用后线程会立即中断吗?
不会,调用 interrupt 只是将被中断线程的中断状态设置为 true,通知被中断的线程自己处理中断,而不是立即强制的让线程直接中断(强制中断不安全)
当外部调用线程进行中断的命令时,如果该线程处于被阻塞的状态,如 Thread.sleep(),Object.wait(),BlockingQueue#put,BlockingQueue#take 等等时,那么此时调用该线程的 interrupt 方法就会抛出 InterruptedException 异常
线程池参数如何设置呢?
线程池参数的设置主要有两个:
- 线程池中
线程数量的设置 任务队列的设置
线程数量的设置:
一般情况下线程数量根据是 CPU 密集还是 IO 密集进行设置即可
对于 核心线程数量 的设置:
- 对于 CPU 密集型任务,将核心线程数量设置为 cpu 核心核心数量 + 1 即可
- 对于 IO 密集型任务,将核心线程数量设置为 cpu 核心数量的两倍
对于 最大线程数量 的设置:
- 最大线程数量设置为核心线程数量一样即可
- 对于 IO 密集型任务,可以将最大线程数量设置的再多一些,避免因为 IO 执行时间过长,导致大量任务阻塞
任务队列的设置:
尽量使用有界队列,避免大量任务进入队列中等待,占用大量内存,并且线程数量会一直保持在核心线程数量,不会创建新的线程去处理任务,增加任务响应时间
具体一点的话,可以根据每秒需要执行的任务数量,以及每个任务执行的时间来设置 核心线程数量
比如 tasks = 500,taskCost = 0.1s,系统容忍最大响应时间 responseTime = 1s,那么每秒就需要执行 500 个任务,每个任务花费 0.1s,那么核心线程数量需要设置为 500 / (1 / taskCost) = 500 * 0.1 = 50,需要 50 个核心线程每秒执行 50 个任务才可以保证在 1s 内将 500 个任务执行完毕
最大线程数量的设置可以根据 (最大任务数 - 队列容量) / 每个线程每秒处理任务数量 来设置
阿里手中的册线程池规范
在使用线程池的时候,需要注意一些规范,以免出现不必要的问题,可以参考阿里巴巴 Java 开发手册,如下:
线程池名称命名规范:
线程池创建规范: