You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

6.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

线程的创建与销毁需要依赖操作系统,其代价是比较高昂的,频繁地创建与销毁线程对系统性能影响较大。 出于线程管理的需要,线程池应运而生。线程池是一种多线程处理形式,处理过程中将任务提交到线程池,任务的执行交由线程池来管理。使用线程池的好处在于:

  • 降低资源消耗:线程池通常会维护一些线程(数量为 corePoolSize这些线程被重复使用来执行不同的任务任务完成后不会销毁。在待处理任务量很大的时候通过对线程资源的复用避免了线程的频繁创建与销毁从而降低了系统资源消耗。
  • 提高响应速度:由于线程池维护了一批 alive 状态的线程,当任务到达时,不需要再创建线程,而是直接由这些线程去执行任务,从而减少了任务的等待时间。
  • 提高线程的可管理性:使用线程池可以对线程进行统一的分配,调优和监控。

一、java中提供的线程池

1.1 Executor

在Java中线程池是由Executor框架实现的Executor是最顶层的接口定义其子类和实现类包括ExecutorServiceScheduledExecutorServiceThreadPoolExecutorScheduledThreadPoolExecutorForkJoinPool等。

  1. ExecutorExecutor是一个接口只定义了一个execute()方法void execute(Runnable command);只能提交Runnable形式的任务不支持提交Callable带有返回值的任务。
  2. ExecutorServiceExecutorService在Executor的基础上加入了线程池的生命周期管理可以通过shutdown或者shutdownNow方法来关闭线程池关于这两个方法后文有详细说明。ExecutorService支持提交Callable形式的任务提交完Callable任务后拿到一个Future代表一个异步任务执行的结果
  3. ThreadPoolExecutor:是线程池中最核心的类,后面有详细说明。
  4. ScheduledThreadPoolExecutorThreadPoolExecutor子类它在ThreadPoolExecutor基础上加入了任务定时执行的功能。

1.2 线程池的创建

Executors中提供了一系列静态方法创建线程池

  • newSingleThreadExecutor:一个单线程的线程池。如果因异常结束,会再创建一个新的,保证按照提交顺序执行。
  • newFixedThreadPool:创建固定大小的线程池。根据提交的任务逐个增加线程,直到最大值保持不变。如果因异常结束,会新创建一个线程补充。 newCachedThreadPool创建一个可缓存的线程池。会根据任务自动新增或回收线程。
  • newScheduledThreadPool:支持定时以及周期性执行任务的需求。
  • newWorkStealingPoolJDK8新增根据所需的并行层次来动态创建和关闭线程通过使用多个队列减少竞争底层使用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进行线程赋值还可能立即创建新的线程来执行任务。