Java线程池前传

2021-04-23 13:02:14 浏览数 (1)

摘要

  1. 为什么要使用线程池
  2. 线程池的总体设计
  3. 线程池的生命周期

1. 为什么要使用线程池

通过线程池,我们可以预留一部分线程,不必要每次创建,使用线程池可以带来以下优点:

  • 降低资源消耗,通过重复利用线程,降低每次线程创建销毁带来的损耗
  • 提高系统响应速度,当我们提交任务时,可以利用已有的线程直接执行,不必等待线程的创建
  • 方便管理线程,通过线程池我们可以管理线程,包括线程的数量,线程需要占用系统资源,如果不加以控制肆意创建,可能会导致系统的崩溃,降低系统的稳定性

2. 线程池的总体设计

上图是线程池的简单类图,ThreadPoolExecutor是整个线程池的核心类,但在介绍它之前我们需要先看一下他实现的接口和继承的抽象类。

2.1 Executor接口

Executor接口中只有一个方法,如下:

代码语言:javascript复制
public interface Executor {

    void execute(Runnable command);
}

该方法其实就是执行一个Task,用来代替普通线程的创建和启动,后面我们会讲解ThreadPoolExecutor中的实现。

2.2 ExecutorService接口

ExecutorService接口继承自Executor接口,在Executor接口功能上,增加了线程池关闭功能、以及为一个异步或一批异步任务生成Future的方法,增加的方法如下图:

2.3 AbstractExecutorService抽象类

AbstractExecutorService抽象类实现了ExecutorService接口,并且实现了ExecutorService接口中的submit、invokeAny、invokeAll方法。

2.4 ThreadPoolExecutor类

ThreadPoolExecutor是线程池的实现类,里面包含很多重要的方法以及属性、和一些特定的内部类,我会在后面剖析线程池的实现过程中逐渐提到这些类和属性。

3. 线程池的生命周期

线程池的生命周期如下表:

状态

描述

RUNNING

运行状态,执行现有任务和阻塞队列中的任务,并且可以接收新的任务

SHUTDOWN

关闭状态,可以执行现有任何和阻塞队列中的任务,但不接收新的任务

STOP

停止状态,中断正在处理任务的线程,不再接收和处理阻塞队列中任务

TIDYING

所有任务已经终止,workCount(工作线程数)为0

TERMINATED

terminated()方法执行完成以后进入该状态

生命周期的转换如下图:

通过上表和上图我们已经清楚的知道了线程池的生命周期,那么线程池的生命周期在ThreadPoolExecutor是如何维护的呢?

代码语言:javascript复制
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    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;

    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

线程池的运行状态由ctl这个属性来表示,但是ctl属性不仅仅维护了线程池的运行状态,还维护了线程数量,那么它是如何同时表示这两个属性的呢?

ctl的高3位保存线程池的运行状态,低29位保存了线程的数量。使用一个变量来存储两个属性,可以避免在做决策时出现不一致的情况,不必为了维护两者的一致性占用锁资源,因为在整个线程的视线中会经常性同时判断工作线程数量和线程池运行状态。

上述属性中COUNT_BITS的值29,CAPACITY就是1左移29位然后减1(536870911),CAPACITY就是最大线程数,如下:

代码语言:javascript复制
00011111 11111111 11111111 11111111

下面的RUNNING、SHUTDOWN、STOP、TIDYING和TERMINATED属性代表线程池的状态。

runStateOf用来计算线程池的状态。

workerCountOf用来计算线程池中工作线程的数量。

ctlOf用来计算ctl的值。

本期的Java线程池基础介绍到这,我是shysh95,我们下期再见!

0 人点赞