操作系统中的状态

- 【初始状态】仅是在语言层面创建了线程对象,还未与操作系统线程关联
- 【可运行状态】(就绪状态)指该线程已经被创建(与操作系统线程关联),可以由 CPU 调度执行
- 【运行状态】指获取了 CPU 时间片运行中的状态
- 当 CPU 时间片用完,会从【运行状态】转换至【可运行状态】,会导致线程的上下文切换
- 【阻塞状态】
- 如果调用了阻塞 API,如 BIO 读写文件,这时该线程实际不会用到 CPU,会导致线程上下文切换,进入【阻塞状态】
- 等 BIO 操作完毕,会由操作系统唤醒阻塞的线程,转换至【可运行状态】
- 与【可运行状态】的区别是,对【阻塞状态】的线程来说只要它们一直不唤醒,调度器就一直不会考虑调度它们
- 【终止状态】表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态
Java中的线程状态
源码定义
1 | /** |
状态转换


- NEW –> RUNNABLE
- 当调用 t.start() 方法时,由 NEW –> RUNNABLE
- RUNNABLE <–> WAITING
- wait方式(需要synchronized(obj)获取锁)
- 释放(obj.notify() , obj.notifyAll() , t.interrupt())
- 竞争锁成功,t 线程从 WAITING –> RUNNABLE
- 竞争锁失败,t 线程从 WAITING –> BLOCKED
- 释放(obj.notify() , obj.notifyAll() , t.interrupt())
- join方式(当前线程在t 线程对象的监视器上等待)
- 释放:运行结束或调用当前线程的interrupt方法:WAITING –> RUNNABLE
- LockSupport.park方式
- 调用unpark(目标线程)或interrupt方法:WAITING –> RUNNABLE
- wait方式(需要synchronized(obj)获取锁)
- RUNNABLE <–> TIMED_WAITING
- wait(时间)方式(需要synchronized(obj)获取锁)
- 释放(obj.notify() , obj.notifyAll() , t.interrupt()、或超时)
- 竞争锁成功,t 线程从 TIMED_WAITING –> RUNNABLE
- 竞争锁失败,t 线程从 TIMED_WAITING –> BLOCKED
- 释放(obj.notify() , obj.notifyAll() , t.interrupt()、或超时)
- join(时间)方式(当前线程在t 线程对象的监视器上等待)
- 释放:运行结束或调用当前线程的interrupt方法、或超时:TIMED_WAITING –> RUNNABLE
- LockSupport.parkNanos(long nanos) 或 LockSupport.parkUntil(long millis)方式
- 调用unpark(目标线程)或interrupt方法、或超时:TIMED_WAITING –> RUNNABLE
- sleep(时间)
- 当前线程超时: TIMED_WAITING –> RUNNABLE
- wait(时间)方式(需要synchronized(obj)获取锁)
- RUNNABLE <–> BLOCKED
- t 线程用 synchronized(obj) 获取了对象锁时如果竞争失败,从 RUNNABLE –> BLOCKED
- 持 obj 锁线程的同步代码块执行完毕,会唤醒该对象上所有 BLOCKED 的线程重新竞争,如果其中 t 线程竞争成功,从 BLOCKED –> RUNNABLE ,其它失败的线程仍然 BLOCKED
- RUNNABLE <–> TERMINATED
- 当前线程所有代码运行完毕,进入 TERMINATED
状态说明
特殊说明
- 创建并启动线程后,不会立即进入就绪状态(需要经过经过新建(New)、就绪(Runnable)、运行(Running)、阻塞 (Blocked)和死亡(Dead)5 种状态。),而且线程的运行需要一些条件(比如内存资源,程序计数器、Java栈、本地方法栈都是线程私有的,所以需要为线程分配一定的内存空间),只有线程运行需要的所有条件满足了,才进入就绪状态。
- 当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。
- 线程在运行状态过程中,可能有多个原因导致当前线程不继续运行下去,比如用户主动让线程睡眠(睡眠一定的时间之后再重新执行)、用户主动让线程等待,或者被同步块给阻塞,此时就对应着多个状态:time waiting(睡眠或等待一定的事件)、waiting(等待被唤醒)、blocked(阻塞)。
- 当由于突然中断或者子任务执行完毕,线程就会被消亡。
状态名称 | 说明 |
---|---|
NEW |
初始状态,线程在被构建时的状态,如果Thread t = new MyThread();还没有调用start方法 此时仅由 JVM 为其分配内存,并初始化其成员变量的值 |
RUNNABLE |
运行状态,java线程将操作系统中的就绪状态和运行状态笼统地称为“运行中”。 ready:当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;此时Java 虚拟机会为其创建方法调用栈和程序计数器,等待调度运行 running:当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态(开始执行run方法)。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中; |
BLOCKED |
阻塞状态,表示线程阻塞于锁,释放出cpu使用权,让出了cpu timeslice,暂时停止运行 此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。 根据阻塞产生的原因不同,阻塞状态又可以分为三种: 1.等待阻塞(o.wait->等待对列):运行状态中的线程执行wait()方法,JVM 会把该线程放入等待队列(waitting queue) 中,使本线程进入到等待阻塞状态; 2.同步阻塞(lock->锁池) :运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用(获取synchronized同步锁失败),则 JVM 会把该线程放入锁池(lock pool)中,进入同步阻塞状态; 3.其他阻塞(sleep/join): 运行(running)的线程执行 Thread.sleep(long ms)或 t.join()方法,或者发出了 I/O 请求时, JVM 会把该线程置为阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入可运行(runnable)状态。 |
WAITING | 等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定的动作(通知或中断) |
TIMED_WAITING | 超时等待状态,改状态不同于waiting,它是可以在指定时间内自动返回的 |
TERMINATED | 线程执行完了或者因异常退出了run()方法,该线程结束生命周期 |
Java多线程的就绪、运行和死亡状态(DEAD)
- 就绪状态转换为运行状态:当此线程得到处理器资源;
- 运行状态转换为就绪状态:当此线程主动调用yield()方法或在运行过程中失去处理器资源。
- 运行状态转换为死亡状态:当此线程线程执行体执行完毕或发生了异常。
- 正常结束 :run()或 call()方法执行完成,线程正常结束。
- 异常结束:线程抛出一个未捕获的 Exception 或 Error。
- 调用stop:直接调用该线程的 stop()方法来结束该线程—该方法通常容易导致死锁,不推荐使用
此处需要特别注意的是:当调用线程的yield()方法时,线程从运行状态转换为就绪状态,但接下来CPU调度就绪状态中的哪个线程具有一定的随机性,因此,可能会出现A线程调用了yield()方法后,接下来CPU仍然调度了A线程的情况。
由于实际的业务需要,常常会遇到需要在特定时机终止某一线程的运行,使其进入到死亡状态。目前最通用的做法是设置一boolean型的变量,当条件满足时,使线程执行体快速执行完毕(不在执行run方法)。
状态分析-jvisualvm
参考代码(java并发编程艺术)
1 | package com.sunld; |
dump信息
1 | 2017-09-28 17:26:47 |