java.lang.Thread类中有个内部枚举类State用来描述线程的各种状态,具体如下
public enum State {
/**
* 尚未启动的线程的状态。
*/
NEW,
/**
* 可运行线程的线程状态。处于可运行状态的某一线程正在Java虚拟机中运行,但它可能正在等待操作系统 * 中的其他资源,比如处理器。
*/
RUNNABLE,
/**
* 受阻塞并且正在等待监视器锁的某一线程的线程状态。处于受阻塞状态的某一线程正在等待监视器锁,以 * 便进入一个同步代码块/方法,或者在调用Object.wait之后再次进入同步代码块/方法。
*/
BLOCKED,
/**
* 某一等待线程的线程状态。某一线程因为调用下列方法之一而处于等待状态:
* <ul>
* <li>不带超时值的Object.wait</li>
* <li>不带超时值的Thread.join</li>
* <li>LockSupport.park<li>
* </ul>
* 处于等待状态的线程正等待另一个线程执行特定操作。 例如,已经在某一对象上调用了Object.wait()的 * 线程正等待另一个线程在该对象上调用Object.notify()或Object.notifyAll()。已经调用了 * Thread.join()的线程正在等待指定线程终止。
*/
WAITING,
/**
* 具有指定等待时间的某一等待线程的线程状态。某一线程因为调用以下带有指定正等待时间的方法之一而处 * 于定时等待状态:
* <ul>
* <li>Thread.sleep</li>
* <li>带有超时值的 Object.wait</li>
* <li>带有超时值的 Thread.join</li>
* <li>LockSupport.parkNanos</li>
* <li>LockSupport.parkUntil</li>
* </ul>
*/
TIMED_WAITING,
/**
* 已终止线程的线程状态。线程已经结束执行。
*/
TERMINATED;
}
下面就用代码呈现各种线程状态
NEW状态
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) { Thread t = new Thread(); System.out.println(t.getState()); } }
输出结果就是
NEW
RUNNABLE状态
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) { Thread t = new Thread(); t.start(); System.out.println(t.getState()); } }
输出结果就是
RUNNABLE
,不过这样并不是每次都能打印出RUNNABLE,也有可能打印出TERMINATED,因为main线程和t线程是并发执行的,有可能在打印线程状态时t线程已经结束运行了。为了总能打印出RUNNABLE,让t线程无限运行下去,代码如下package cn.cjc.multithread; public class MainTest { public static void main(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { while (true) ;//无限循环 } }); t.start(); System.out.println(t.getState()); } }
还有一种查看线程状态的方法,就是用JVM命令查看,对于上面的代码,程序会一直运行,所以可以用jstack命令dump出线程状态,操作如下
1)用jps命令查看JVM进程的pid,可知pid=4069
2)用jstack命令打印线程栈信息,可以看到Thread-0的线程处于RUNNABLE状态需要注意的是线程状态是RUNNABLE不代表该线程正在运行中,有可能该线程时间片用完,正在等待CPU,也就是处于就绪状态。所以RUNNABLE有可能是正在运行中,也有可能是就绪状态。
BLOCKED状态
线程调用synchronized方法或者进入synchronized代码块时,获取不到监视器锁而阻塞,则产生该状态
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) { final MainTest mainTest = new MainTest(); new Thread(new Runnable() { @Override public void run() { mainTest.m1(); } }, "thread-m1").start(); new Thread(new Runnable() { @Override public void run() { mainTest.m2(); } }, "thread-m2").start(); } public synchronized void m1() { System.out.println("m1"); while (true) ; } public synchronized void m2() { System.out.println("m2"); while (true) ; } }
运行后用jstack命令查看线程状态发现thread-m1线程是RUNNABLE状态,而thread-m2线程是BLOCKED (on object monitor)状态,因为同步锁一直被thread-m1持有,thread-m2在等待锁
还有一种情况也会产生BLOCKED状态,代码如下
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) throws InterruptedException { final MainTest mainTest = new MainTest(); new Thread(new Runnable() { @Override public void run() { mainTest.m1(); } }, "thread-m1").start(); new Thread(new Runnable() { @Override public void run() { mainTest.m2(); } }, "thread-m2").start(); Thread.sleep(3000); new Thread(new Runnable() { @Override public void run() { mainTest.m3(); } }, "thread-m3").start(); } public synchronized void m1() { System.out.println("m1---start"); try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m1---end"); while (true) ; } public synchronized void m2() { System.out.println("m2---start"); try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m2---end"); while (true) ; } public synchronized void m3() { System.out.println("m3"); this.notifyAll();//注意不是this.notify()哦 } }
运行后用jstack命令打印如下
发现thread-m1线程是BLOCKED (on object monitor)状态,这和第一种情况不是一样吗?其实不一样,看此处thread-m1线程栈的第一行有个in Object.wait(),而第一种情况thread-m2线程栈的第一行是waiting for monitor entry,有啥区别?
第二种情况产生过程是这样:m1线程先持有锁,然后释放锁并等待被唤醒,紧接着m2线程持有锁,然后释放锁并等待被唤醒,m3线程调用notifyAll方法唤醒了所有等待着的线程,这时候m2线程拿到锁并一直持有,m1线程因为被唤醒了,但没有拿到锁,所以从WAITING状态变成了BLOCKED状态。
第一种情况下线程BLOCKED,是在Entry Set里面,第二种情况线程BLOCKED,是在Wait Set里面。
我要说还有第三种情况的BLOCKED,会不会被打?真有呢!就是调用带超时时间的Object.wait且看代码
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) { final MainTest mainTest = new MainTest(); new Thread(new Runnable() { @Override public void run() { mainTest.m1(); } }, "thread-m1").start(); new Thread(new Runnable() { @Override public void run() { mainTest.m2(); } }, "thread-m2").start(); } public synchronized void m1() { System.out.println("m1---start"); try { this.wait(30000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m1---end"); while (true) ; } public synchronized void m2() { System.out.println("m2---start"); try { this.wait(30000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m2---end"); while (true) ; } }
运行起来后,30s内用jstack命令查看线程m1和m2的状态都是WAITING (on object monitor),但是30s以后查看线程状态,如图示
thread-m2线程处在BLOCKED状态,但是阻塞原因第二情况是in Object.wait(),而这个是waiting for monitor entry,我以为会和第二种情况一样(为什么会不一样呢?后面有时间再深入看看),纳尼?这不是和第一种进入synchronized同步块的情况一样吗?确实很像,但是从阻塞线程栈的第三行往下看就可以区分开来了。
WAITING状态
WAITING状态在线程栈中有两种不同的体现,分别为WAITING (on object monitor)和WAITING (parking)。
1)调用Object.wait()产生WAITING (on object monitor)状态,代码如下
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) { final MainTest mainTest = new MainTest(); new Thread(new Runnable() { @Override public void run() { mainTest.m1(); } }, "thread-m1").start(); } public synchronized void m1() { System.out.println("m1---start"); try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m1---end"); } }
运行后的线程栈如下
2)调用Thread.join产生WAITING (on object monitor)状态,代码如下
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(new Runnable() { @Override public void run() { while (true) ; } }); t.start(); t.join();//等待t线程结束后再往下执行 System.out.println("main---end"); } }
运行后线程栈如下
3)调用java.util.concurrent.locks.Lock#lock方法会产生WAITING (parking)状态,代码如下
package cn.cjc.multithread; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class MainTest { private Lock lock = new ReentrantLock(); public static void main(String[] args) { final MainTest mainTest = new MainTest(); new Thread(new Runnable() { @Override public void run() { mainTest.m1(); } }, "thread-m1").start(); new Thread(new Runnable() { @Override public void run() { mainTest.m2(); } }, "thread-m2").start(); } public void m1() { lock.lock(); try { System.out.println("m1---start"); while (true) ; } finally { lock.unlock(); System.out.println("m1---end"); } } public void m2() { lock.lock(); try { System.out.println("m2---start"); while (true) ; } finally { lock.unlock(); System.out.println("m2---end"); } } }
运行后的线程栈如下
可以发现thread-m1线程在运行中,而thread-m2线程没有获得锁处于WAITING状态,这个和进入synchronized方法或代码块时没有获得锁产生的状态是完全不一样的。
另外java.util.concurrent.locks.Condition#await()方法也会产生WAITING (parking)状态,
java.util.concurrent.BlockingQueue#put
和java.util.concurrent.BlockingQueue#take
底层调用的就是此方法。其实它们底层调用的都是LockSupport.park方法。TIMED_WAITING状态
TIMED_WAITING状态要细分的话,会有三种,分别是TIMED_WAITING (sleeping)、TIMED_WAITING (on object monitor)、TIMED_WAITING (parking)
1)Thread.sleep方法会产生第一种状态;
2)带超时时间的Object.wait和带超时时间的Thread.join方法会产生第二种状态;
3)带超时时间的lock.tryLock和带超时时间的Condition.await方法会产生第三种状态,带超时时间的
java.util.concurrent.BlockingQueue#offer
和java.util.concurrent.BlockingQueue#poll
的底层调用的就是带超时时间的Condition.awaitNanos方法。TERMINATED状态
这个状态最好理解,如代码
package cn.cjc.multithread; public class MainTest { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(); t.start(); Thread.sleep(3000); System.out.println(t.getState()); } }
输出结果就是
TERMINATED