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

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

一、说明


线程池的生命周期


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


  • ctl高三位保存运行状态(23=8>5),低29位保存工作线程的数量(229-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自定义线程池

目录
相关文章
|
2天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
4天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
4天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
4天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
56 2
|
7月前
|
安全 Java
深入理解Java并发编程:线程安全与性能优化
【2月更文挑战第22天】在Java并发编程中,线程安全和性能优化是两个重要的主题。本文将深入探讨这两个主题,包括线程安全的基本概念,如何实现线程安全,以及如何在保证线程安全的同时进行性能优化。
65 0
|
7月前
|
存储 安全 Java
深入理解Java并发编程:线程安全与锁机制
【5月更文挑战第31天】在Java并发编程中,线程安全和锁机制是两个核心概念。本文将深入探讨这两个概念,包括它们的定义、实现方式以及在实际开发中的应用。通过对线程安全和锁机制的深入理解,可以帮助我们更好地解决并发编程中的问题,提高程序的性能和稳定性。
|
4月前
|
存储 安全 Java
解锁Java并发编程奥秘:深入剖析Synchronized关键字的同步机制与实现原理,让多线程安全如磐石般稳固!
【8月更文挑战第4天】Java并发编程中,Synchronized关键字是确保多线程环境下数据一致性与线程安全的基础机制。它可通过修饰实例方法、静态方法或代码块来控制对共享资源的独占访问。Synchronized基于Java对象头中的监视器锁实现,通过MonitorEnter/MonitorExit指令管理锁的获取与释放。示例展示了如何使用Synchronized修饰方法以实现线程间的同步,避免数据竞争。掌握其原理对编写高效安全的多线程程序极为关键。
71 1
|
5月前
|
安全 Java 开发者
Java并发编程中的线程安全问题及解决方案探讨
在Java编程中,特别是在并发编程领域,线程安全问题是开发过程中常见且关键的挑战。本文将深入探讨Java中的线程安全性,分析常见的线程安全问题,并介绍相应的解决方案,帮助开发者更好地理解和应对并发环境下的挑战。【7月更文挑战第3天】
109 0
|
6月前
|
安全 Java 开发者
Java并发编程中的线程安全策略
在现代软件开发中,Java语言的并发编程特性使得多线程应用成为可能。然而,随着线程数量的增加,如何确保数据的一致性和系统的稳定性成为开发者面临的挑战。本文将探讨Java并发编程中实现线程安全的几种策略,包括同步机制、volatile关键字的使用、以及java.util.concurrent包提供的工具类,旨在为Java开发者提供一系列实用的方法来应对并发问题。
53 0
|
7月前
|
安全 Java 容器
Java一分钟之-并发编程:线程安全的集合类
【5月更文挑战第19天】Java提供线程安全集合类以解决并发环境中的数据一致性问题。例如,Vector是线程安全但效率低;可以使用Collections.synchronizedXxx将ArrayList或HashMap同步;ConcurrentHashMap是高效线程安全的映射;CopyOnWriteArrayList和CopyOnWriteArraySet适合读多写少场景;LinkedBlockingQueue是生产者-消费者模型中的线程安全队列。注意,过度同步可能影响性能,应尽量减少共享状态并利用并发工具类。
71 2