为什么用线程池,优势

线程池做的工作主要就是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程的数量超过了最大数量,超过数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。
主要特点:线程复用;控制最大并发数;管理线程。
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗;
第二:提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即工作;
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以同一的分配、调优和监控。

JDK编码实现

  1. package com.interview.demo;
  2. import java.util.concurrent.ExecutorService;
  3. import java.util.concurrent.Executors;
  4. /**
  5. * @Author leijs
  6. * @date 2022/3/29
  7. */
  8. public class MyThreadPoolDemo {
  9. public static void main(String[] args) {
  10. // 固定线程数的池子,线程池有5个线程
  11. ExecutorService threadPool = Executors.newFixedThreadPool(5);
  12. // ExecutorService threadPool = Executors.newSingleThreadExecutor();
  13. // ExecutorService threadPool = Executors.newCachedThreadPool();
  14. // 执行短周期的任务
  15. // ExecutorService threadPool = Executors.newScheduledThreadPool(2);
  16. try {
  17. for (int i = 0; i < 10; i++) {
  18. threadPool.execute(() -> {
  19. System.out.println(Thread.currentThread().getName() + "\t 办理业务");
  20. });
  21. }
  22. } catch (Exception e) {
  23. } finally {
  24. threadPool.shutdown();
  25. }
  26. }
  27. }

底层:ThreadPoolExecutor
image.png
注意:LinkedBlockingQueue

线程池七大参数

image.png

参数 解释
corePoolSize 线程池中的核心线程数。
- 在创建了线程池后,当有请求任务来之后,就会安排池中的线程去执行请求任务
- 当线程中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列中。
maximumPoolSize 线程池能够容纳同时执行的最大线程数,此值必须大于等于1
keepAliveTime 多余的空闲线程的存活时间。当前线程池数量超过corePoolSize时,当空闲时间达到keepAliveTime时,多余空闲线程会被销毁直到只剩下corePoolSize个线程为止。
- 默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize
TimeUnit keepAliveTime的单位
workQueue 任务队列(阻塞队列);被提交但尚未被执行的任务
Executors.defaultThreadFactory() 表示生成线程池中工作线程的线程工厂,用于创建线程,一般用默认的即可
defaultHandler > private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();

拒绝策略:表示当队列满了并且工作线程大于等于最大线程数(maximumPoolSize)
- AbortPolicy
- DiscardPolicy
- CallerRunsPolicy
- DiscardOldestPolicy
|

线程池工作原理

image.png
image.png

拒绝策略

内置的拒绝策略都实现了RejectedExecutionHandler接口

AbortPolicy(默认)

直接抛出异常RejectedExecutionException, 异常阻止系统正常运行

CallRunsPolicy

“调用者运行”一种调节机制,该策略不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量

DiscardOldestPolicy

抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务

DiscardPolicy

直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案

工作中使用哪一种线程池?

JDK默认的四种都不用
image.png
image.png
结论:自己定义。

  1. ExecutorService executorService = new ThreadPoolExecutor(2,
  2. 10, 60, TimeUnit.SECONDS,
  3. new LinkedBlockingDeque<Runnable>(100),
  4. Executors.defaultThreadFactory(),
  5. new ThreadPoolExecutor.CallerRunsPolicy());

线程池参数设置

  • CPU密集型

指该任务需要大量的运算,没有阻塞,CPU一直运行。
一般公式:CPU核数+1 的线程池

  • IO密集型

并不知一直在执行的任务,IO会有大量的阻塞。尽可能配置多: CPU核数 * 2
参考核数: CPU核数 / (1 - 阻塞系数)
阻塞系数 0.8~0.9 一般乐观一点取个0.9