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