ThreadPoolExecutor的构造方法详解以及应急线程的解释

2022-08-02,,,,

线程池状态

// 线程池的控制状态,用高3位来表示线程池的运行状态,低29位来表示线程池中工作线程的数量
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
	private static final int COUNT_BITS = Integer.SIZE - 3; // 29

  // 高三位来表示线程的运行状态
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

构造方法

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 
  • corePoolSize:表示核心线程数的数量
  • maximumPoolSize:表示最大的线程数
  • keepAliveTime:表示应急线程的存活时间
  • unit:表示应急线程的存活时间的单位
  • workQueue:阻塞队列,就是线程忙不过来的时候,任务要在队列里排队
  • threadFactory:线程工厂,可以为线程创建的时候起个好名字
  • handler:拒绝策略

keepAliveTime和unit是针对应急线程的,那么什么是应急线程呢?

先说一下线程池里线程的类型。

  • 核心线程
  • 应急线程

以下代码,我在线程池里放了2个核心线程,最大线程数为3(也就是说应急线程为1),阻塞队列的容量为2,此时运行下面的代码查看结果。

public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,3,1000L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(2));

        pool.execute(() -> {
            try {
                System.out.println("第一个任务");
                Thread.sleep(1000000L);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        });

        pool.execute(() -> {
            try {
                System.out.println("第二个任务");
                Thread.sleep(1000000L);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        });

        pool.execute(() -> {
            try {
                System.out.println("第三个任务");
                Thread.sleep(1000000L);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        });

        pool.execute(() -> {
            try {
                System.out.println("第四个任务");
                Thread.sleep(1000000L);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        });
    }

发现只有两个任务被线程消费了,但是不是有三个线程吗???另一个线程不工作???

这两个任务是被两个核心线程消费的,而剩余的两个任务被放到阻塞队列里(阻塞队列的容量是2),这时候的情况不算“应急”的情况,所以应急线程并没有启动。

那什么时候算“应急”??
此时我们再加一个任务瞅瞅,2个核心线程在工作,空间为2的队列也满了,多出来来的任务怎么办呢??应急线程会出手吗?

		pool.execute(() -> {
            try {
                System.out.println("第五个任务");
                Thread.sleep(100000L);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        });

查看结果发现三个任务被消费了~其实是应急线程出来应急了。

总结一下:如果队列选择了有界队列,而任务超过了队列大小时,会创建 maximumPoolSize - corePoolSize 数目的应急线程来救急。而应急当然就是救急用的,所以当高峰过去而没有任务做,就会被结束,由keepAliveTime 和 unit 来控制。

拒绝策略
如果任务量特别多,核心线程+应急线程(maximumPoolSize )+ 队列大小都撑不住,这时候就会执行拒绝策略了。

  • AbortPolicy:丢弃任务并抛出RejectedExecutionException异常,默认策略;
  • CallerRunsPolicy:由调用execute方法的线程执行该任务;
  • DiscardPolicy:丢弃任务,但是不抛出异常;
  • DiscardOldestPolicy:丢弃阻塞队列最前面的任务,然后重新尝试执行任务(重复此过程)。
  • 实现RejectedExecutionHandler接口,自定义相关处理的过程,如记录日志或持久化存储不能处理的任务。

提交任务


// 执行一个任务
void execute(Runnable command) 

// submit实质还是执行了execute方法,获取返回结果
Future<?> submit(Runnable task)
<T> Future<T> submit(Runnable task, T result)
<T> Future<T> submit(Callable<T> task)

//执行所有的任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,  long timeout, TimeUnit unit)  throws InterruptedException

// 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消 
<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)  throws InterruptedException, ExecutionException, TimeoutException;

本文地址:https://blog.csdn.net/axiang_/article/details/107341060

《ThreadPoolExecutor的构造方法详解以及应急线程的解释.doc》

下载本文的Word格式文档,以方便收藏与打印。