线程池

线程池 && CountDownLatch

线程池

1. 七大参数

    public ThreadPoolExecutor(int corePoolSize,//线程池的核心线程数量
                              int maximumPoolSize,//线程池的最大线程数
                              long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间
                              TimeUnit unit,//时间单位
                              BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列
                              ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可
                              RejectedExecutionHandler handler//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务)

2. 阻塞队列

3. 拒绝策略

4. 运行状态

5. execute和submit

1. execute只能提交Runnable类型的任务,无返回值。

2. submit既可以提交Runnable类型的任务,也可以提交Callable类型的任务。提交Callable类型任务,会有一个类型为Future的返回值。但当任务类型为Runnable时,返回值为null。
3. execute在执行任务时,如果遇到异常会直接抛出,而submit不会直接抛出,只有在使用Future的get方法获取返回值时,才会抛出异常。

6. 继承关系

  • Executor:顶层抽象接口。将任务提交执行解耦。用户无需关注如何创建线程,如何调度线程来执行任务,用户只需提供Runnable对象,将任务的运行逻辑提交到执行器(Executor)中,由Executor框架完成线程的调配和任务的执行部分。

  • ExecutorService:接口。增加了一些能力(1)扩充执行任务的能力,补充可以为一个或一批异步任务生成Future的方法;(2)提供了管控线程池的方法,比如停止线程池的运行

  • AbstractExecutorService:上层抽象类。将执行任务的流程串联了起来,保证下层的实现只需关注一个执行任务的方法即可。

  • ThreadPoolExecutor:实现类。一方面维护自身的生命周期,另一方面同时管理线程和任务,使两者良好的结合从而执行并行任务。

7.Executors

// 可重用固定线程数的线程池
Executors.newFixedThreadPool();
// 单例线程池
Executors.newSingleThreadExecutor();
// 缓存线程池
Executors.newSingleThreadExecutor();
// 在给定的延迟后运行任务,或者定期执行任务。
Executors.newScheduledThreadPool();

《阿里巴巴 Java 开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 构造函数的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险

Executors 返回线程池对象的弊端如下(后文会详细介绍到):

  • FixedThreadPoolSingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。两者静态方法均是使用ThreadPoolExecutor构造函数,传递了一个无界LinkedBlockingQueue,阻塞长度为Integer.MAX。
  • CachedThreadPoolScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。两者静态方法均是使用ThreadPoolExecutor构造函数,传递的线程池最大线程数量为Integer.MAX。

8. 确定线程池大小

CPU 密集型任务(N+1):比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。

I/O 密集型任务(2N):大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N。

cpu密集型:简单理解就是利用 CPU 计算能力的任务比如你在内存中对大量数据进行排序。

io密集型:但凡涉及到网络读取,文件读取这类都是 IO 密集型,这类任务的特点是 CPU 计算耗费时间相比于等待 IO 操作完成的时间来说很少,大部分时间都花在了等待 IO 操作完成上。

9. 工作流程

10. 修改线程池线程名称

  • CustomizableThreadFactory

    Spring 框架提供的 CustomizableThreadFactory

 ThreadFactory springThreadFactory = new CustomizableThreadFactory("springThread-pool-");
     
 ExecutorService exec = new ThreadPoolExecutor(1, 1,
         0L, TimeUnit.MILLISECONDS,
         new LinkedBlockingQueue<Runnable>(10),springThreadFactory);

 exec.submit(() -> {
     logger.info("--记忆中的颜色是什么颜色---");
 });
  • ThreadFactoryBuilder

    Google guava 工具类 提供的 ThreadFactoryBuilder ,使用链式方法创建。

     ThreadFactory guavaThreadFactory = 
     			new ThreadFactoryBuilder().setNameFormat("retryClient-pool-").build();
     
     ExecutorService exec = new ThreadPoolExecutor(1, 1,
             0L, TimeUnit.MILLISECONDS,
             new LinkedBlockingQueue<Runnable>(10),guavaThreadFactory );
             
     exec.submit(() -> {
         logger.info("--记忆中的颜色是什么颜色---");
     });
    

11. 动态化参数

通过分布式配置中心动态修改参数。

动态化线程池的核心设计包括以下三个方面:

  1. 简化线程池配置:线程池构造参数有8个,但是最核心的是3个:corePoolSize、maximumPoolSize,workQueue,它们最大程度地决定了线程池的任务分配和线程分配策略。考虑到在实际应用中我们获取并发性的场景主要是两种:(1)并行执行子任务,提高响应速度。这种情况下,应该使用同步队列,没有什么任务应该被缓存下来,而是应该立即执行。(2)并行执行大批次任务,提升吞吐量。这种情况下,应该使用有界队列,使用队列去缓冲大批量的任务,队列容量必须声明,防止任务无限制堆积。所以线程池只需要提供这三个关键参数的配置,并且提供两种队列的选择,就可以满足绝大多数的业务需求,Less is More。
  2. 参数可动态修改:为了解决参数不好配,修改参数成本高等问题。在Java线程池留有高扩展性的基础上,封装线程池,允许线程池监听同步外部的消息,根据消息进行修改配置。将线程池的配置放置在平台侧,允许开发同学简单的查看、修改线程池配置。
  3. 增加线程池监控:对某事物缺乏状态的观测,就对其改进无从下手。在线程池执行任务的生命周期添加监控能力,帮助开发同学了解线程池状态。

其他

线程池内部的一个变量维护着两个值:运行状态(runState)和线程数量 (workerCount)。高3位保存runState,低29位保存workerCount

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

shutdown()VSshutdownNow()

  • shutdown() :关闭线程池,线程池的状态变为 SHUTDOWN。线程池不再接受新任务了,但是队列里的任务得执行完毕。
  • shutdownNow() :关闭线程池,线程的状态变为 STOP。线程池会终止当前正在运行的任务,并停止处理排队的任务并返回正在等待执行的 List。

isTerminated() VS isShutdown()

  • isShutDown 当调用 shutdown() 方法后返回为 true。
  • isTerminated 当调用 shutdown() 方法后,并且所有提交的任务完成后返回为 true

CountDownLatch

知乎:https://zhuanlan.zhihu.com/p/148610594

javaguide: https://javaguide.cn/java/concurrent/java-concurrent-questions-02.html#_6-aqs

参考
知乎:https://zhuanlan.zhihu.com/p/123328822

javaguide:https://javaguide.cn/java/concurrent/java-thread-pool-summary/

posted @ 2023-02-17 18:04  OraCat  阅读(10)  评论(0编辑  收藏  举报