java多线程解说【拾肆】_线程池

本文详细介绍了Java中线程池的实现方式及使用方法,包括Executor框架的各种接口和类,如ThreadPoolExecutor、ScheduledThreadPoolExecutor等,并讲解了如何通过Executors工厂类创建不同类型的线程池。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


在多线程编程的实际开发中,我们势必会需要启动多个线程来处理多个任务。因为线程的创建和销毁需要一定的性能开销,如果每次执行一个任务都创建一个新线程来执行,那么将消耗大量的计算资源。

所以我们需要一个线程池的概念,让使用过的线程可以回收再使用,避免重复创建带来的性能消耗。下面说一下juc包中为我们提供的线程池实现:


Executor框架


juc包中用ThreadPoolExecutor表示一个线程池,任何实现Runnable接口对象都可以被ThreadPoolExecutor调度。而我们通过Executors线程池工厂,可以取得一个特定功能的线程池。juc包中关于线程池的类关系图如下:




Executor

一个接口,其定义了一个接收Runnable对象的方法executor;


ExecutorService

一个比Executor使用更广泛的子类接口,其提供了生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回Future的方法


AbstractExecutorService

ExecutorService执行方法的默认实现


ThreadPoolExecutor

线程池的核心实现类,用来执行被提交的任务;


ScheduledExecutorService

一个可定时调度任务的接口


ScheduledThreadPoolExecutor

ScheduledExecutorService的实现,一个可定时调度任务的线程池,可以指定延迟执行或周期执行;
 
ThreadFactory

用来实现创建线程的工厂接口,只有一个方法Thread newThread(Runnable r);
 

Runnable、Callable

两个接口,其实现类可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行;
 

ForkJoinPool

用于拆分子任务执行的线程池;上文已经介绍过


Future、FutureTask

用于异步计算;上文已经介绍过


那么这个Executor框架如何使用呢?


Executor框架的使用


首先看一张示意图:






我们的任务线程,必定是实现Runnable或Callable接口二者之一的,区别在于Runnable不会返回结果,而Callable可以返回结果。我们还可以通过Executors工厂类将一个Runnable包装成一个Callable:


public static Callable<Object> callable(Runnable task)

public static <T> Callable<T> callable(Runnable task, T result)


这两个方法区别在于,第一个方法用Future对象调用get()方法时将获得null;而第二个方法将得到result。


说回RunnableCallable。我们可以将一个Runnable对象通过execute()接口提交给ThreadPoolExecutor或ScheduledThreadPoolExecutor执行,或将一个Callable对象通过submit()接口提交给ThreadPoolExecutor或ScheduledThreadPoolExecutor执行。如前文所说,submit()接口会返回一个Future对象,调用其get(0方法可以获得到这个Callable对象的执行结果。


那么这个ThreadPoolExecutor或ScheduledThreadPoolExecutor线程池应该如何创建呢,这就要说说Executors工厂类为我们提供的创建线程池的API了。


创建线程池


我们已经知道,Executors工厂类可以其创建ThreadPoolExecutor或ScheduledThreadPoolExecutor两种线程池。先说说ThreadPoolExecutor的创建接口:


newFixedThreadPool(int nThreads)
返回一个固定线程数量nThreads的线程池。一个新任务提交时,若线程池内线程数未达到nThreads,则创建新线程执行;如果线程数已达到nThreads且有空闲线程时,使用空闲线程执行;如果没有空闲线程,则任务被暂存在一个任务队列(LinkedBlockingQueue,无界队列)中,等有线程空闲时再执行。线程池中的线程执行完毕后立即终止;


newSingleThreadExecutor
返回一个只有一个线程的线程池。一个新任务提交时,若线程池内线程数为0,则创建新线程执行;如果线程池内线程空闲,则使用空闲线程执行;如果没有空闲线程,则任务被暂存在一个任务队列(LinkedBlockingQueue,无界队列)中,等有线程空闲时再执行。线程池中的线程执行完毕后立即终止


newCachedThreadPool
返回一个根据实际情况调整线程数量的线程池,初始化时线程池内没有线程。每提交一个新任务时,如果线程池为空或没有空闲线程,则创建一个新的线程处理。线程池的最大容量为0x7fffffff(int最大值,32位最大带符号整数),空闲线程如果60s内没有任务分配将被终止。

Executors工厂类还可以创建ScheduledThreadPoolExecutor线程池。ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,可指定延迟执行或周期执行。功能和Timer类型,但是强大在ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数,而Timer只能是单个后台线程。


ScheduledThreadPoolExecutor有如下2个创建ScheduledThreadPoolExecutor的接口:


newSingleThreadScheduledExecutor
返回一个ScheduledExecutorService对象,线程池大小为1;


newScheduledThreadPool(int corePoolSize)
返回一个ScheduledExecutorService对象,可以执行线程数量;



ThreadPoolExecutor和ScheduledThreadPoolExecutor的实现


ThreadPoolExecutor的构造函数如下:


 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue)

其中,

corePoolSize是核心线程池大小;

maximumPoolSize是最大线程池大小;

workQueue是用来战术保存任务的工作队列;

keepAliveTime是为多余的空闲线程等待新任务的最长时间;

unit是指定keepAliveTime参数的单位;

handle是线程池对拒绝任务的处理策略;


ThreadPoolExecutor的三个实现中,FixedThreadPool和SingleThreadExecutor使用无界队列LinkedBlockingQueue作为线程池的工作队列,而CachedThreadPool使用没有容量的SynchronousQueue作为线程池的工作队列,不过SynchronousQueue的maximumPool是无界的。所以,当提交任务的速度大于线程处理任务的速度时,CachedThreadPool将不断地创建新线程。


ScheduledThreadPoolExecutor的构造方法为:


public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
              new DelayedWorkQueue());
    }

其只传了一个核心线程池大小corePoolSize,其余的参数默认设定为不限制最大线程池大小,线程执行完任务后直接终止,并且使用DelayQueue来作为线程池的工作队列。


DelayQueue也是一个无界序列,其中保存的是待调度的任务ScheduledFutureTask。ScheduledFutureTask主要包含三个成员变量,包括:


time:long型,表示这个任务将被执行的具体时间;

sequenceNumber:long型,表示添加到ScheduledThreadPoolExecutor的序号;

period,long型,表示任务执行的间隔周期;


DelayQueue中还封装了一个PriorityQueue队列,将ScheduledFutureTask按time从小到达排列。每次ScheduledThreadPoolExecutor去获取或添加任务时,会先获取锁,然后进行任务操作,然后再释放锁。


任务拒绝策略


ThreadPoolExecutor.execute(Runnable command)提供了提交任务的入口,此方法会自动判断如果池子满了的话,则会调用拒绝策略来执行此任务,接口为RejectedExecutionHandler,内置的4中策略分别为AbortPolicy、DiscardPolicy、DiscardOldestPolicy、CallerRunsPolicy。


AbortPolicy
为java线程池默认的阻塞策略,不执行此任务,而且直接抛出java.util.concurrent.RejectedExecutionException异常,切记ThreadPoolExecutor.execute需要try catch,否则程序会直接退出;


DiscardPolicy
直接抛弃,任务不执行,空方法;


DiscardOldestPolicy
从队列里面抛弃head的一个任务,并再次execute此task;


CallerRunsPolicy

在调用execute的线程里面执行此command;如果执行程序已关闭,则会丢弃该任务。会阻塞入口;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值