从源码解读线程(Thread)和线程池(ThreadPoolExecutor)的状态

线程是比进程更加轻量级的调度执行单位,理解线程是理解并发编程的不可或缺的一部分;而生产过程中不可能永远使用裸线程,需要线程池技术,线程池是管理和调度线程的资源池。因为前不久遇到了一个关于线程状态的问题,今天就趁热打铁从源码的层面来谈一谈线程和线程池的状态及状态之间的转移。

线程

JDK中,线程(Thread)定义了6种状态: NEW(新建)、RUNNABLE(可执行)、BLOCKED(阻塞)、WAITING(等待)、TIMED_WAITING(限时等待)、TERMINATED(结束)。
源码如下:

    /**
     * A thread state.  A thread can be in one of the following  states:
     * <ul>
     * <li>{@link #NEW}

     *     A thread that has not yet started is in this state.
     *     </li>
     * <li>{@link #RUNNABLE}

     *     A thread executing in the Java virtual machine is in  this state.
     *     </li>
     * <li>{@link #BLOCKED}

     *     A thread that is blocked waiting for a monitor lock
     *     is in this state.
     *     </li>
     * <li>{@link #WAITING}

     *     A thread that is waiting indefinitely for another  thread to
     *     perform a particular action is in this state.
     *     </li>
     * <li>{@link #TIMED_WAITING}

     *     A thread that is waiting for another thread to perform  an action
     *     for up to a specified waiting time is in this state.
     *     </li>
     * <li>{@link #TERMINATED}

     *     A thread that has exited is in this state.
     *     </li>
     * </ul>
     *
     * <p>
     * A thread can be in only one state at a given point in  time.
     * These states are virtual machine states which do not  reflect
     * any operating system thread states.
     *
     * @since   1.5
     * @see #getState
     */
    public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,
        
        /**
         * Thread state for a runnable thread.  A thread in the  runnable
         * state is executing in the Java virtual machine but it  may
         * be waiting for other resources from the operating  system
         * such as processor.
         */
        RUNNABLE,
        
        /**
         * Thread state for a thread blocked waiting for a  monitor lock.
         * A thread in the blocked state is waiting for a monitor  lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,
        
        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of  the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no  timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for  another thread to
         * perform a particular action.
         *
         * For example, a thread that has called  <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or  <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called  <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,
        
        /**
         * Thread state for a waiting thread with a specified  waiting time.
         * A thread is in the timed waiting state due to calling  one of
         * the following methods with a specified positive  waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with  timeout</li>
         *   <li>{@link #join(long) Thread.join} with  timeout</li>
         *   <li>{@link LockSupport#parkNanos  LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil  LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,
        
        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

状态说明

线程在一个给定的时间点只能处于下面其中一种状态:
这些状态是虚拟机状态,并不能反映任何操作系统的线程状态。

  • NEW:尚未启动的线程处于这个状态。
  • RUNNABLE:正在Java虚拟机中执行的线程处于这个状态。
  • BLOCKED:阻塞,等待监视器锁的线程处于这个状态。
  • WAITING:无限期等待另一个线程执行特定操作的线程处于这种状态。
  • TIMED_WAITING:正在等待另一个线程执行某个操作的线程在指定的等待时间内处于这种状态。
  • TERMINATED:已经退出的线程处于这个状态。

状态转移

NEW:线程尚未启动的线程状态。当在程序中创建一个线程的时候Thread t = new Thread(Runnable);,线程处于NEW状态。
RUNNABLE:可运行线程的线程状态。处于可运行状态的线程正在Java虚拟机中执行,但它可能正在等待操作系统中的其他资源,比如处理器。也就是说, 这个状态就是可运行可不运行的状态。注意Runnable ≠ Running。
BLOCKED:等待监视器锁的阻塞线程的线程状态。比如,线程试图通过synchronized去获取某个锁,但是其他线程已经独占了,那么当前线程就会处于阻塞状态。处于阻塞状态的线程正在等待监视器锁去进入同步块/方法(等待一个监视器锁时为了进入同步块/方法),或者在调用Object.wait()后重新进入同步块/方法。
WAITING:调用以下方法之一,线程会处于等待状态:

  • Object.wait()注意:括号内不带参数;
  • Thread.join()注意:扩号内不带参数;
  • LockSupport.park();

其实wait()方法有多重形式,可以不带参数,可以带参数,参数表示等待时间(单位ms),如图所示:

“BLOCKED(阻塞状态)”和“WAITING(等待状态)”的区别:阻塞状态在等待获取一个排它锁,这个事件将会在另外一个线程放弃这个锁的时候发生,然后由阻塞状态变为可执行状态;而等待状态则是在等待一段时间,或者等待唤醒动作的发生。
TIMED_WAITING:一个线程调用了以下方法之一(方法需要带具体的等待时间),会处于定时等待状态:Thread.sleep(long timeout)Object.wait(long timeout)Thread.join(long timeout)LockSupport.parkNanos()LockSupport.parkUntil()TERMINATED: 该线程已经执行完毕。其实这些大部分在源码的注释中可以找到。下面我自己翻译的中文版,不嫌弃的话可以参考:

    /**
     * 线程状态。  一个线程可以处于下列状态之一:
     *
     * NEW:尚未启动的线程处于这个状态。
     *     
     * RUNNABLE:正在Java虚拟机中执行的线程处于这个状态。
     *
     * BLOCKED:阻塞中,等待监视器锁的线程处于这个状态。
     *
     * WAITING:无限期等待另一个线程执行特定操作的线程处于这种状态。
     *
     * TIMED_WAITING:正在等待另一个线程执行某个操作的线程在指定的等待时间内处于这种状态。
     *
     * TERMINATED:已经退出的线程处于这个状态。
     *
     * 线程在一个给定的时间点只能处于一种状态。
     * 这些状态是虚拟机状态,并不能反映任何操作系统的线程状态。
     *
     */
    public enum State {
        /**
         * 线程尚未启动的线程状态。
         */
        NEW,


        /**
         * 可运行线程的线程状态。处于可运行状态的线程正在Java虚拟机中执行,
         * 但它可能正在等待操作系统中的其他资源,比如处理器。
         */
        RUNNABLE,


        /**
         * 等待监视器锁的阻塞线程的线程状态。
         * 处于阻塞状态的线程正在等待监视器锁进入同步块/方法,
         * 或者在调用Object.wait后重新进入同步块/方法。
         */
        BLOCKED,


        /**
         * 等待线程的线程状态。
         * 调用以下方法之一,线程会处于等待状态:
         *   Object.wait()注意:括号内不带参数;
         *   Thread.join()注意:扩号内不带参数;
         *   LockSupport.park();
         *
         * 处于等待状态的线程正在等待另外一个线程执行特定的操作。
         *
         * 例如,一个调用了object.wait()方法的线程正在等待另外一下线程调用
         * object.notify()或者object.notifyAll()方法。注意,这两个object是同一个object。
         * 一个调用了Thread.join()方法的线程正在等待一个特定的线程去终止。
         */
        WAITING,


        /**
         * 具有指定等待时间的等待线程的线程状态。
         * 一个线程调用了以下方法之一(方法需要带具体的等待时间),会处于定时等待状态:
         *   Thread.sleep(timeout)
         *   Object.wait(timeout)
         *   Thread.join(timeout)
         *   LockSupport.parkNanos()
         *   LockSupport.parkUntil()
         */
        TIMED_WAITING,


        /**
         * 终止的线程状态。
         * 该线程已经执行完毕。
         */
        TERMINATED;
    }

状态转移图如图所示:


线程状态转移图

线程池

在生产环境中,为每个任务分配一个线程是存在缺陷的,例如资源消耗和稳定性等,所以需要使用线程池。Java类库提供了灵活的线程池,可以调用Executors中的静态工厂方法创建线程池。如:

  • newFixedThreadPool:固定长度的线程池
  • newCachedThreadPool:可缓存的线程池。

不管是newFixedThreadPool还是newCachedThreadPool,底层都是通过ThreadPoolExecutor实现的,本文只谈ThreadPoolExecutor的状态。
在JDK源码中,线程池(ThreadPoolExecutor)定义了五种状态:RUNNING、SHUTDOWN、STOP、TIDYING和TERMINATED。
源码如下:

    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;

状态说明

RUNNING :运行状态,可以添加新任务,也可以处理阻塞队列中的任务。
SHUTDOWN :待关闭状态,不再接受新的任务,会继续处理阻塞队列中的任务。
STOP :停止状态,不再接受新的任务,不会执行阻塞队列中的任务,打断正在执行的任务。
TIDYING:整理状态,所有任务都处理完毕,workerCount为0,线程转到该状态将会运行terminated()钩子方法。
TERMINATED:终止状态,terminated()方法执行完毕。

状态转移

     * RUNNING -> SHUTDOWN
     *    On invocation of shutdown(), perhaps implicitly in  finalize()
     * (RUNNING or SHUTDOWN) -> STOP
     *    On invocation of shutdownNow()
     * SHUTDOWN -> TIDYING
     *    When both queue and pool are empty
     * STOP -> TIDYING
     *    When pool is empty
     * TIDYING -> TERMINATED
     *    When the terminated() hook method has completed

线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0。
当线程池处于RUNNING状态时,调用shutdown()方法,线程池RUNNING状态转为SHUTDOWN状态。
当线程池处于RUNNING or SHUTDOWN时,调用shutdownNow()方法时,线程池由(RUNNING or SHUTDOWN )状态转为STOP状态。
当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN状态转为TIDYING状态。
当线程池处于STOP状态,当线程池中执行的任务为空的时候,线程池有STOP状态转为TIDYING状态。
当线程池处于TIDYING状态,当执行完terminated()之后,就会由TIDYING状态转为TERMINATED状态。

状态转移图如图所示:
线程池状态转移图

总结

理解线程和线程池对于我们日常开发或者诊断分析,都是不可或缺的基础。本文从源码分析了线程和线程池的状态和各种方法之间的对应关系,希望对大家有帮助,文中如果有地方不妥还请大家指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352