2.8 线程的状态
一个Thread对象可以有多个状态,在java.lang.Thread.State
中,总共定义六种状态:
1、NEW
线程刚刚被创建,也就是已经new过了,但是还没有调用start()方法
2、RUNNABLE
RUNNABLE这个名字很具有欺骗性,很容易让人误以为处于这个状态的线程正在运行。事实上,这个状态只是表示,线程是可运行的。我们已经无数次提到过,一个单核CPU在同一时刻,只能运行一个线程。
3、BLOCKED
线程处于阻塞状态,正在等待一个monitor lock。通常情况下,是因为本线程与其他线程公用了一个锁。其他在线程正在使用这个锁进入某个synchronized同步方法块或者方法,而本线程进入这个同步代码块也需要这个锁,最终导致本线程处于阻塞状态。
4、WAITING
等待状态,调用以下方法可能会导致一个线程处于等待状态:
Object.wait
with no timeoutThread.join
with no timeout
例如:对于wait()方法,一个线程处于等待状态,通常是在等待其他线程完成某个操作。本线程调用某个对象的wait()方法,其他线程处于完成之后,调用同一个对象的notify或者notifyAll()方法。Object.wait()方法只能够在同步代码块中调用。调用了wait()方法后,会释放锁。
5、TIMED_WAITING
线程等待指定的时间,对于以下方法的调用,可能会导致线程处于这个状态:
Object.wait
with timeoutThread.join
with timeout
6、TERMINATED
线程终止。
这些状态中NEW状态是开始,TERMINATED时销毁,在整个线程对象的运行过程中,这个两个状态只能出现一次。其他任何状态都可以出现多次,彼此之间可以相互转换。
Tips:
经常有人把线程的状态又称为线程的
生命周期
。个人认为这种叫法并不好,因为生命周期通常指的是一个对象从创建到销毁所必须要经历的过程。例如J2EE的Servlet的生命周期方法是init->service->destroy,这三个阶段是必须要经历的。而对于线程而言,完全可以NEW了之后就直接到TERMINATED了,或者只经历其中某个状态而不是所有。因此这可能也是官方只称之为线程状态(用java.lang.Thread.State类表示)的原因。不过,因为生命周期这个叫法在某种程度上又有一些说明力,笔者也不反对你也这样来称呼线程的不同状态。总之,需要知道的一点是,别人和你提到线程状态或者是线程生命周期,其实指的是同一个东西。
接下来,我们将通过jps
,jstack
工具演示说明线程的各个状态,首先我们准备一些测试代码:
public class ThreadState { public static void main(String[] args) { new Thread(new Running(), "RunningThread").start(); new Thread(new TimeWaiting(), "TimeWaitingThread").start(); new Thread(new Waiting(), "WaitingThread-1").start(); new Thread(new Waiting(), "WaitingThread-2").start(); // 使用两个Blocked线程,一个获取锁成功,另一个被阻塞 new Thread(new Blocked(), "BlockedThread-1").start(); new Thread(new Blocked(), "BlockedThread-2").start(); } /** * 表示线程正在运行 * @author TIANSHOUZHI336 * */ static class Running implements Runnable{ @Override public void run() { for (int i = 0; i < Long.MAX_VALUE; i++) { i++; } } } /** * 该线程不断的进行睡眠 */ static class TimeWaiting implements Runnable { @Override public void run() { while (true) { SleepUtils.second(100); } } } /** * 该线程在Waiting.class实例上等待 */ static class Waiting implements Runnable { @Override public void run() { while (true) { synchronized (Waiting.class) { try { Waiting.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } /** * 该线程在Blocked.class实例上加锁后,不会释放该锁 */ static class Blocked implements Runnable { public void run() { synchronized (Blocked.class) { while (true) { SleepUtils.second(100); } } } } public static class SleepUtils { public static final void second(long seconds) { try { TimeUnit.SECONDS.sleep(seconds); } catch (InterruptedException e) { } } } }
代码分析:
RunningThread
线程运行一个不断自增加法,持续时间会很长,线程一直应该处于RUNNABLE状态。
TimeWaiting
线程里面是一个死循环,每次休眠100毫秒,因此大部分情况下,应该处于TIME-WAITING状态。
WaitingThread-1
和WaitingThread-2
共同竞争一个类锁:Waiting.class。而同步快里面,又调用了wait()方法,先得到锁的线程会释放锁,因此最终2个线程都处于Waiting状态。
BlockedThread-1
和BlockedThread-2
线程也是共同竞争一个类锁:Blocked.class。而同步快里面,是一个死循环,然后调用了SleepUtils.sleep()方法,因此一直不会释放锁。所以二者,应该是有一个大部分情况下处于Time-Waiting状态,另一个处于Blocked状态。
通过jps与jtack分析:
C:\Users\tianshouzhi>jps 6240 ThreadState 6576 Jps 2568 org.eclipse.equinox.launcher_1.3.0.v20140415-2008.jar C:\Users\tianshouzhi>jstack 6240 >>1.txt
jps命令用户查看当前系统中运行的java进程。jstack工具用于追踪线程堆栈信息。上述操作,将线程堆栈信息输入1.txt文件中,在这个文件里,我们可以看到以下信息:
"BlockedThread-2" #17 prio=5 os_prio=0 tid=0x14d90400 nid=0x1584 waiting on condition [0x15e7f000] java.lang.Thread.State: TIMED_WAITING (sleeping)//调用sleep方法导致的TIME-WAITING at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at chapter04.SleepUtils.second(SleepUtils.java:11) at chapter04.ThreadState$Blocked.run(ThreadState.java:91) - locked <0x048f1380> (a java.lang.Class for chapter04.ThreadState$Blocked) at java.lang.Thread.run(Thread.java:745)
"BlockedThread-1" #16 prio=5 os_prio=0 tid=0x14d8ec00 nid=0x420 waiting for monitor entry [0x15cef000] java.lang.Thread.State: BLOCKED (on object monitor)//锁竞争导致的阻塞,在等待锁 at chapter04.ThreadState$Blocked.run(ThreadState.java:91) - waiting to lock <0x048f1380> (a java.lang.Class for chapter04.ThreadState$Blocked) at java.lang.Thread.run(Thread.java:745)
"WaitingThread-2" #15 prio=5 os_prio=0 tid=0x14d7b800 nid=0x1bf8 in Object.wait() [0x15d4f000] java.lang.Thread.State: WAITING (on object monitor)//已经被锁住,但是还要再次获取,注意观察下面的locked紫色标记部分 at java.lang.Object.wait(Native Method) - waiting on <0x048efab0> (a java.lang.Class for chapter04.ThreadState$Waiting) at java.lang.Object.wait(Object.java:502) at chapter04.ThreadState$Waiting.run(ThreadState.java:75) - locked <0x048efab0> (a java.lang.Class for chapter04.ThreadState$Waiting) at java.lang.Thread.run(Thread.java:745)
"WaitingThread-1" #14 prio=5 os_prio=0 tid=0x14d7b000 nid=0x18e4 in Object.wait() [0x15c6f000] java.lang.Thread.State: WAITING (on object monitor)//已经被锁住,但是还要再次获取 at java.lang.Object.wait(Native Method) - waiting on <0x048efab0> (a java.lang.Class for chapter04.ThreadState$Waiting) at java.lang.Object.wait(Object.java:502) at chapter04.ThreadState$Waiting.run(ThreadState.java:75) - locked <0x048efab0> (a java.lang.Class for chapter04.ThreadState$Waiting) at java.lang.Thread.run(Thread.java:745)
"TimeWaitingThread" #13 prio=5 os_prio=0 tid=0x14d7a400 nid=0xc50 waiting on condition [0x15b9f000] java.lang.Thread.State: at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at chapter04.SleepUtils.second(SleepUtils.java:11) at chapter04.ThreadState$TimeWaiting.run(ThreadState.java:61) at java.lang.Thread.run(Thread.java:745)
" java.lang.Thread.State: RUNNABLE //正常运行 at chapter04.ThreadState$Running.run(ThreadState.java:48) at java.lang.Thread.run(Thread.java:745) |
线程状态转换图