线程池
Java 线程池是一种多线程处理技术,它可以在程序中预先创建一定数量的线程,将任务提交到线程池中,线程池会自动调度线程执行任务。通过使用线程池,可以避免反复创建和销毁线程的开销,提高程序性能,同时也能够避免线程资源的浪费和过度使用。
为什么使用线程池?
在并发环境下,系统不能够确定在任意时刻中,有多少任务需要执行,有多少资源需要投入。这种不确定性将带来以下若干问题:
1、频繁申请/销毁资源和调度资源,将带来额外的消耗,可能会非常巨大。
2、对资源无限申请缺少抑制手段,易引发系统资源耗尽的风险。
3、系统无法合理管理内部的资源分布,会降低系统的稳定性。
Java 线程池的主要优势包括:
1、重用线程:线程池中的线程可以被重复利用,避免了创建和销毁线程的开销,提高了程序的性能。
2、提高程序的响应速度:当有任务到来时,线程池中已经有了准备好的线程,可以立即处理任务,而不需要等待线程的创建。
3、提高资源利用率:由于线程的创建和销毁是一项昂贵的操作,使用线程池可以避免不必要的资源浪费。
Java线程池涉及内容
java中涉及到线程池的相关类均在jdk1.5开始的java.util.concurrent包中,涉及到的几个核心类及接口包括:Executor、Executors、ExecutorService、ThreadPoolExecutor、FutureTask、Callable、Runnable等。
这些接口和类都是Java中用于实现线程池的核心组件,它们的作用如下:
1、Executor 接口定义了一种将任务提交与任务执行分离的标准方式,它只有一个方法 execute(Runnable command),用于执行传入的任务。
2、Executors 类是一个工厂类,提供了创建各种类型线程池的静态工厂方法。例如,newFixedThreadPool(int nThreads) 方法可以创建一个固定大小的线程池。
3、ExecutorService 接口继承自 Executor 接口,提供了更加丰富的任务执行管理方法,例如可以通过 submit(Callable task) 方法提交一个有返回值的任务,并返回一个 Future 对象来表示任务的执行结果。
4、ThreadPoolExecutor 类是一个线程池的具体实现,实现了 ExecutorService 接口。它可以根据需要自动调整线程池中的线程数量,以满足当前任务的需求。
5、FutureTask 类表示一个异步计算任务,它实现了 RunnableFuture 接口,可以在执行完成后返回一个结果。它可以被提交给 Executor 或 ExecutorService 进行执行。
6、Callable 接口类似于 Runnable 接口,但它可以返回一个值,并且可以抛出异常。
7、Runnable 接口表示一个可以被执行的任务。
Java线程池通常由以下四个组件组成:
任务队列:用于存放需要执行的任务。
线程池管理器:用于创建、销毁线程池,并控制线程池的状态。
工作线程:实际执行任务的线程。
任务:需要执行的具体业务逻辑。
线程池的创建及重要参数
Java 线程池的实现主要通过 Executor 框架提供的线程池接口实现。常用的线程池类型包括:
1、FixedThreadPool:固定大小的线程池,可以控制并发线程的数量。
2、CachedThreadPool:可以根据需要创建新的线程,但在可用线程的数量超过处理需求时,线程池会回收空闲的线程。
3、ScheduledThreadPool:用于执行周期性任务和延迟任务,支持定时执行任务和循环执行任务。
4、SingleThreadExecutor:只有一个工作线程的线程池,可以保证所有的任务按照指定的顺序在一个线程中执行。
public static ExecutorService newFixedThreadPool(int var0) {return new ThreadPoolExecutor(var0, var0, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());}public static ExecutorService newSingleThreadExecutor() {return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()));}public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue());}public static ScheduledExecutorService newScheduledThreadPool(int var0) {return new ScheduledThreadPoolExecutor(var0);}
Java中的线程池核心实现类是ThreadPoolExecutor。
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {……}
使用线程池的关键是合理地配置线程池的参数,例如线程池的大小、任务队列的大小、线程的超时时间等。如果线程池的参数设置不当,可能会导致线程池中的线程数量不足或者过多,从而影响程序的性能。
ThreadPoolExecutor实现的顶层接口是Executor,顶层接口Executor提供了一种思想:将任务提交和任务执行进行解耦。用户无需关注如何创建线程,如何调度线程来执行任务,用户只需提供Runnable对象,将任务的运行逻辑提交到执行器(Executor)中,由Executor框架完成线程的调配和任务的执行部分。ExecutorService接口增加了一些能力:(1)扩充执行任务的能力,补充可以为一个或一批异步任务生成Future的方法;(2)提供了管控线程池的方法,比如停止线程池的运行。AbstractExecutorService则是上层的抽象类,将执行任务的流程串联了起来,保证下层的实现只需关注一个执行任务的方法即可。最下层的实现类ThreadPoolExecutor实现最复杂的运行部分,ThreadPoolExecutor将会一方面维护自身的生命周期,另一方面同时管理线程和任务,使两者良好的结合从而执行并行任务。
ThreadPoolExecutor是如何运行,如何同时维护线程和执行任务的呢?其运行机制如下图所示:
corePoolSize:核心线程数,也是线程池中常驻的线程数,线程池初始化时默认是没有线程的,当任务来临时才开始创建线程去执行任务
maximumPoolSize:最大线程数,在核心线程数的基础上可能会额外增加一些非核心线程,需要注意的是只有当workQueue队列填满时才会创建多于corePoolSize的线程(线程池总线程数不超过maxPoolSize)
keepAliveTime:非核心线程的空闲时间超过keepAliveTime就会被自动终止回收掉,注意当corePoolSize=maxPoolSize时,keepAliveTime参数也就不起作用了(因为不存在非核心线程);
unit:keepAliveTime的时间单位
workQueue:用于保存任务的队列,可以为无界、有界、同步移交三种队列类型之一,当池子里的工作线程数大于corePoolSize时,这时新进来的任务会被放到队列中
threadFactory:创建线程的工厂类,默认使用Executors.defaultThreadFactory(),也可以使用guava库的ThreadFactoryBuilder来创建
handler:线程池无法继续接收任务(队列已满且线程数达到maximunPoolSize)时的饱和策略,取值有AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy
workQueue队列
SynchronousQueue(同步移交队列):队列不作为任务的缓冲方式,可以简单理解为队列长度为零
LinkedBlockingQueue(无界队列):队列长度不受限制,当请求越来越多时(任务处理速度跟不上任务提交速度造成请求堆积)可能导致内存占用过多或OOM
ArrayBlockintQueue(有界队列):队列长度受限,当队列满了就需要创建多余的线程来执行任务
handler拒绝策略
AbortPolicy:中断抛出异常
DiscardPolicy:默默丢弃任务,不进行任何通知
DiscardOldestPolicy:丢弃掉在队列中存在时间最久的任务
CallerRunsPolicy:让提交任务的线程去执行任务(对比前三种比较友好一丢丢)
关闭线程池
shutdownNow():立即关闭线程池(暴力),正在执行中的及队列中的任务会被中断,同时该方法会返回被中断的队列中的任务列表
shutdown():平滑关闭线程池,正在执行中的及队列中的任务能执行完成,后续进来的任务会被执行拒绝策略
isTerminated():当正在执行的任务及对列中的任务全部都执行(清空)完就会返回true
springboot使用线程池
/*** @Description 配置类中构建线程池实例,方便调用*/
@Configuration
public class ThreadPoolConfig {@Bean(value = "threadPoolInstance")public ExecutorService createThreadPoolInstance() {//通过guava类库的ThreadFactoryBuilder来实现线程工厂类并设置线程名称ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("thread-pool-%d").build();ExecutorService threadPool = new ThreadPoolExecutor(10, 16, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100), threadFactory, new ThreadPoolExecutor.AbortPolicy());return threadPool;}
}
//通过name=threadPoolInstance引用线程池实例@Resource(name = "threadPoolInstance")private ExecutorService executorService;@Overridepublic void spikeConsumer() {//TODOexecutorService.execute(new Runnable() {@Overridepublic void run() {//TODO}});}
.其它相关
在ThreadPoolExecutor类中有两个比较重要的方法引起了我们的注意:beforeExecute和afterExecute
protected void beforeExecute(Thread var1, Runnable var2) {}protected void afterExecute(Runnable var1, Throwable var2) {
}