线程池的执行流程

什么是线程池?

线程池内部维护了若干个线程,没有任务的时候,这些线程都处于等待空闲状态。如果有新的线程任务,就分配一个空闲线程执行。如果所有线程都处于忙碌状态,线程池会创建一个新线程进行处理或者放入队列(工作队列)中等待。

在Java标准库提供了如下几个类或接口,来操作并使用线程池:


ExecutorService接口:进行线程池的操作访问;
Executors类:创建线程池的工具类;
ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制;

 execute()只能提交Runnable类型的任务,没有返回值,而submit()既能提交Runnable类型任务也能提交Callable类型任务,可以返回Future类型结果,用于获取线程任务执行结果。
execute()方法提交的任务异常是直接抛出的,而submit()方法是捕获异常,当调用Future的get()方法获取返回值时,才会抛出异常。

线程池的执行流程

1.提交一个新线程任务,线程池会在线程池中分配一个空闲线程,用于执行线程任务;
2.如果线程池中不存在空闲线程,则线程池会判断当前“存活的线程数”是否小于核心线程数corePoolSize。
(1)如果小于核心线程数corePoolSize,线程池会创建一个新线程(核心线程)去处理新线程任务;
(2)如果大于核心线程数corePoolSize,线程池会检查工作队列
3.如果工作队列未满,则将该线程任务放入工作队列进行等待。线程池中如果出现空闲线程,将从工作队列中按照FIFO的规则取出1个线程任务并分配执行;
4.如果工作队列已满,则判断线程数是否达到最大线程数maximumPoolSize;
5.如果当前“存活线程数”没有达到最大线程数maximumPoolSize,则创建一个新线程(非核心线程)执行新线程任务;
6.如果当前“存活线程数”已经达到最大线程数maximumPoolSize,直接采用拒绝策略处理新线程任务;

 线程池的状态:

首先,在ThreadPoolExecutor类中有一个Integer类型的原子变量ctl,用来记录线程池状态和线程池中线程个数。其中高3位用来表示线程池状态,低29位用来记录线程池线程个数。

RUNNING:接收新任务并且处理阻塞队列里的任务。

SHUTDOWN:拒绝新任务但是处理阻塞队列里的任务。

STOP:拒绝新任务并且抛弃阻塞队列里的任务,同时会中断正在处理的任务。

TIDYING:所有任务都执行完(包含阻塞队列里的任务)后当前线程池活动线程数为0,将要调用terminated()方法。

TERMINATED:终止状态,terminated()方法调用完成以后的状态。

线程池状态转换

RUNNING->SHUTDOWN:显式调用了shutdown()方法,或者隐式调用了finalize()方法里面的shutdown()方法。

RUNNING/SHUTDOWN->STOP:显式调用了shutdownNow()方法。

SHUTDOWN->TIDYING:当线程池和任务队列都为空时。

STOP->TIDYING:当线程池为空时。

TIDYING->TERMINATED:当terminated()方法执行完成时。

 几种常见的线程池

SingleThreadExecutor:

SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}


创建单个线程。它适用于需要保证顺序地执行各个任务;并且在任意时间点,不会有多个线程是活动的应用场景。SingleThreadExecutor的corePoolSize和maximumPoolSize被设置为1,使用无界队列LinkedBlockingQueue作为线程池的工作队列。

当线程池中没有线程时,会创建一个新线程来执行任务。
当前线程池中有一个线程后,将新任务加入LinkedBlockingQueue
线程执行完第一个任务后,会在一个无限循环中反复从LinkedBlockingQueue 获取任务来执行。

 使用场景:适用于串行执行任务场景

FixedThreadPool:

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>(),
                                  threadFactory);
}

corePoolSize等于maximumPoolSize,所以线程池中只有核心线程,使用无界阻塞队列LinkedBlockingQueue作为工作队列

FixedThreadPool是一种线程数量固定的线程池,当线程处于空闲状态时,他们并不会被回收,除非线程池被关闭。当所有的线程都处于活动状态时,新的任务都会处于等待状态,直到有线程空闲出来。

如果当前运行的线程数少于corePoolSize,则创建新线程来执行任务。
在线程数目达到corePoolSize后,将新任务放到LinkedBlockingQueue阻塞队列中。
线程执行完(1)中任务后,会在循环中反复从LinkedBlockingQueue获取任务来执行。

 使用场景:适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务。

 CachedThreadPool:

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

核心线程数为0,总线程数量阈值为Integer.MAX_VALUE,即可以创建无限的非核心线程

先执行SynchronousQueue的offer方法提交任务,并查询线程池中是否有空闲线程来执行SynchronousQueue的poll方法来移除任务。如果有,则配对成功,将任务交给这个空闲线程
否则,配对失败,创建新的线程去处理任务
当线程池中的线程空闲时,会执行SynchronousQueue的poll方法等待执行SynchronousQueue中新提交的任务。若等待超过60s,空闲线程就会终止

使用场景:执行大量短生命周期任务。因为maximumPoolSize是无界的,所以提交任务的速度 > 线程池中线程处理任务的速度就要不断创建新线程;每次提交任务,都会立即有线程去处理,因此CachedThreadPool适用于处理大量、耗时少的任务。

ScheduledThreadPoolExecutor:

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

线程总数阈值为Integer.MAX_VALUE,工作队列使用DelayedWorkQueue,非核心线程存活时间为0,所以线程池仅仅包含固定数目的核心线程。

两种方式提交任务:

scheduleAtFixedRate: 按照固定速率周期执行

scheduleWithFixedDelay:上个任务延迟固定时间后执行

 使用场景:周期性执行任务,并且需要限制线程数量的场景

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

说好陪我数星星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值