写点什么

Java 多线程 线程池的生命周期及运行状态

作者:Yeats_Liao
  • 2022-10-18
    江西
  • 本文字数:4535 字

    阅读完需:约 1 分钟

一、说明

线程池的生命周期


  • 线程池的状态runState和工作线程数量workerCount共同保存在 AtomicInteger 类型的控制变量 ctl

  • ctl 高三位保存运行状态(2^3^=8>5),低 29 位保存工作线程的数量(2^29^-1)


  // 初始运行状态为RUNNING,线程数为0    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));    // COUNT_BITS: 29    private static final int COUNT_BITS = Integer.SIZE - 3;    // CAPACITY: 十进制: 536870911 二进制: 00011111111111111111111111111111    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits // RUNNING: 十进制:-536870912 二进制:11100000000000000000000000000000 private static final int RUNNING = -1 << COUNT_BITS; // SHUTDOWN: 十进制:0 二进制:0 private static final int SHUTDOWN = 0 << COUNT_BITS; // STOP: 十进制:536870912 二进制:00100000000000000000000000000000 private static final int STOP = 1 << COUNT_BITS; // TIDYING: 十进制:1073741824 二进制:01000000000000000000000000000000 private static final int TIDYING = 2 << COUNT_BITS; // TERMINATED: 十进制:1610612736 二进制:01100000000000000000000000000000 private static final int TERMINATED = 3 << COUNT_BITS; // Packing and unpacking ctl 打包和解包ctl // 获取线程池当前状态,CAPACITY取反,高三位都是1,低29位都是0,和ctl进行与运算,获得runState变量 private static int runStateOf(int c) { return c & ~CAPACITY; } // CAPACITY高三位都是0,低29位都是0,和ctl进行与运算获得workerCount变量 private static int workerCountOf(int c) { return c & CAPACITY; } // 初始化ctl变量,runState和workerCount进行或运算后共同存储在一个变量中 private static int ctlOf(int rs, int wc) { return rs | wc; }
复制代码


  • RUNNING 接收新的任务,并且可执行队列里的任务

  • SHUTDOWN 停止接收新任务,但可执行队列里的任务

  • STOP 可执行队列里的任务,不执行队列里的任务,中断正在执行的任务

  • TIDYING 所有任务都已终止,线程数为 0,当线程池变为 TIDYING 状态时,会执行钩子函数 terminated(),钩子方法是指使用一个抽象类实现接口,一个抽象类实现这个接口,需要的方法设置为 abstract,其它设置为空方法

  • TERMINATED 终止状态,表示线程池已关闭,已经执行完 terminated()钩子方法


判断当前线程池运行状态


  // 判断线程池当前运行状态是否小于给定值    private static boolean runStateLessThan(int c, int s) {        return c < s;    }  // 判断线程池当前运行状态是否大于等于给定值    private static boolean runStateAtLeast(int c, int s) {        return c >= s;    }  // 判断线程池是否处于RUNNING状态    private static boolean isRunning(int c) {        return c < SHUTDOWN;    }   // 判断线程池是否处于SHUTDOWN状态    public boolean isShutdown() {        return ! isRunning(ctl.get());    }   // 判断线程池是否处于TERMINATING状态    public boolean isTerminating() {        int c = ctl.get();        return ! isRunning(c) && runStateLessThan(c, TERMINATED);    }   // 判断线程池是否处于TERMINATED状态    public boolean isTerminated() {        return runStateAtLeast(ctl.get(), TERMINATED);    }
复制代码


运行状态转换关系


二、理解

RUNNING


接收新的任务,并且可执行队列里的任务


  // 初始运行状态为RUNNING,线程数为0    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
复制代码


SHUTDOWN


shutdown()方法将线程池状态转换为SHUTDOWN,停止接收新任务,但可执行队列里的任务


public void shutdown() {    // 上锁确保只有一个线程执行此操作    final ReentrantLock mainLock = this.mainLock;    mainLock.lock();    try {        // 检查是否有权限关闭线程池以和中断线程        checkShutdownAccess();        // 将线程池状态设置为SHUTDOWN        advanceRunState(SHUTDOWN);        // 中断所有空闲线程        interruptIdleWorkers();        // 用于取消延时任务        onShutdown(); // hook for ScheduledThreadPoolExecutor    } finally {        mainLock.unlock();    }    // 将线程池置为TERMINATED状态    tryTerminate();}
复制代码


    private void checkShutdownAccess() {         // 检查是否有安全管理器,确保调用者有权关闭线程        SecurityManager security = System.getSecurityManager();        if (security != null) {            security.checkPermission(shutdownPerm);            // 上锁确保只有一个线程执行此操作            final ReentrantLock mainLock = this.mainLock;            mainLock.lock();            try {           // 对工作的线程做安全权限检查                for (Worker w : workers)                    security.checkAccess(w.thread);            } finally {                mainLock.unlock();            }        }    }
复制代码


    private void advanceRunState(int targetState) {        // assert targetState == SHUTDOWN || targetState == STOP;        // CAS 自旋操作,死循环将线程池运行状态设置为目标值        for (;;) {            // 获取AtomicInteger 类型的控制变量 ctl             int c = ctl.get();            // 判断当前线程池的状态值是否大于目标值            if (runStateAtLeast(c, targetState) ||\            // 将线程池运行状态设置为目标值,成功的话会返回true,失败则返回false                ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))                break;        }    }
复制代码


  // 中断所有空闲线程    private void interruptIdleWorkers() {        interruptIdleWorkers(false);    }
复制代码


  // 如果参数为false,中断所有空闲线程,如果为true,则只中断一个空闲线程    private void interruptIdleWorkers(boolean onlyOne) {        // 上锁确保只有一个线程执行此操作        final ReentrantLock mainLock = this.mainLock;        mainLock.lock();        try {            // 检查所有的工作线程是否被中断            for (Worker w : workers) {                Thread t = w.thread;                // 如果没有被中断,并且工作线程获得了锁,则执行中断方法,并释放线程锁                // 成功则返回true,表示处于无锁状态的worker(state为0)为空闲线程                // 失败则返回false,表示处于锁定状态的worker(state为1)为工作线程,                if (!t.isInterrupted() && w.tryLock()) {                    try {                        t.interrupt();                    } catch (SecurityException ignore) {                    } finally {                        w.unlock();                    }                }                // 如果为true,表示只中断一个空闲线程,退出循环,适用于用tryTerminate()方法                if (onlyOne)                    break;            }        } finally {            mainLock.unlock();        }    }
复制代码


STOP


shutdownNow()方法将线程池状态转换为STOP,同时中断所有线程,停止接收新任务,不执行队列里的任务,中断正在执行的任务


public List<Runnable> shutdownNow() {    List<Runnable> tasks;    // 上锁确保只有一个线程执行此操作    final ReentrantLock mainLock = this.mainLock;    mainLock.lock();    try {        // 检查是否有权限关闭线程池以和中断线程        checkShutdownAccess();        // 将线程池运行状态置为STOP        advanceRunState(STOP);        // 中断所有线程        interruptWorkers();        // 将未执行的任务移入列表中        tasks = drainQueue();    } finally {        mainLock.unlock();    }    // 将线程池置为TERMINATED状态    tryTerminate();    return tasks;}
复制代码


TIDYING


所有任务都已终止,线程数为 0,线程池变为TIDYING状态,会执行钩子函数terminated(),钩子方法是指使用一个抽象类实现接口,一个抽象类实现这个接口,需要的方法设置为abstract,其它设置为空方法


//  每个工作线程的终结都会调用tryTerminate()方法final void tryTerminate() {    for (;;) {        int c = ctl.get();        // 判断线程池的状态,下面几种情况则直接返回        // 1. 线程池处于RUNNING状态        // 2. 线程池处于TIDYING或者TERMINATED状态,意味着已经走到了下面的步骤,线程池即将终结        // 3. 线程池处于SHUTDOWN状态且任务队列不为空        if (isRunning(c) ||            runStateAtLeast(c, TIDYING) ||            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))            return;        // 到这里说明当前线程池除了线程数不为0,其他条件都已经满足关闭要求        // 如果工作线程数量不为0,则尝试中断工作线程集合中的一个空闲线程        if (workerCountOf(c) != 0) {            interruptIdleWorkers(ONLY_ONE);            return;        }        // 到这里说明线程池满足所有关闭条件的要求,接下来将线程池运行状态置为TERMINATED       // 上锁确保只有一个线程执行此操作        final ReentrantLock mainLock = this.mainLock;        mainLock.lock();        try {            // CAS设置线程池运行状态为TIDYING            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {                try {                    // 执行terminated()钩子方法,用于终止线程池                    terminated();                } finally {             // 将线程池运行状态置为TERMINATED,且线程数为0                    ctl.set(ctlOf(TERMINATED, 0));                    // 唤醒阻塞在termination条件的所有线程                    termination.signalAll();                }                return;            }        } finally {            mainLock.unlock();        }        // else retry on failed CAS    }}
复制代码


termination实现 Condition 接口,用于实现线程阻塞和唤醒


private final ReentrantLock mainLock = new ReentrantLock();private final Condition termination = mainLock.newCondition();
复制代码


TERMINATED


执行完terminated()钩子方法,线程池已终止,变为TERMINATED状态

三、实现

使用ThreadPoolExecutor自定义线程池


详见:Java多线程 ThreadPoolExecutor自定义线程池

发布于: 刚刚阅读数: 3
用户头像

Yeats_Liao

关注

Hello,World! 2022-10-02 加入

这里更多的是记录个人学习,如果有侵权内容请联系我! 个人邮箱是:yeats_liao@foxmail.com

评论

发布
暂无评论
Java多线程 线程池的生命周期及运行状态_后端_Yeats_Liao_InfoQ写作社区