線程狀態(tài)及其轉(zhuǎn)換
一勋锤、線程狀態(tài)
Java中定義線程的狀態(tài)有6種,可以查看Thread類的State枚舉:
public static enum State
{
NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED;
private State() {}
}
- 初始(NEW):新創(chuàng)建了一個(gè)線程對(duì)象侥祭,還沒(méi)調(diào)用start方法叁执;
- 運(yùn)行(RUNNABLE):java線程中將就緒(ready)和運(yùn)行中(running)統(tǒng)稱為運(yùn)行(RUNNABLE)。線程創(chuàng)建后調(diào)用了該對(duì)象的start方法矮冬,此時(shí)處于就緒狀態(tài)谈宛,當(dāng)獲得CPU時(shí)間片后變?yōu)檫\(yùn)行中狀態(tài);
- 阻塞(BLOCKED):表現(xiàn)線程阻塞于鎖胎署;
- 等待(WAITING):進(jìn)入該狀態(tài)的線程需要等待其他線程做出一些特定動(dòng)作(通知或中斷)吆录;
- 超時(shí)等待(TIMED_WAITING):該狀態(tài)不同于WAITING,它可以在指定時(shí)間后自行返回琼牧;
- 終止(TERMINATED):表示該線程已經(jīng)執(zhí)行完畢恢筝。
二哀卫、線程狀態(tài)轉(zhuǎn)換
來(lái)看一張線程狀態(tài)轉(zhuǎn)換圖:
下面從代碼實(shí)例看線程的各個(gè)狀態(tài):
2.1 超時(shí)等待
public class Test {
public static void main(String[] args) throws Exception {
System.out.println("start");
Thread.sleep(100000);
System.out.println("end");
}
}
通過(guò)Java VisualVM打印線程dump可以看到此線程處于TIMED_WAITING
狀態(tài):
...
"main" #1 prio=5 os_prio=0 tid=0x00000000055b3800 nid=0x4e8c waiting on condition [0x000000000558f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at Test.main(Test.java:4)
Locked ownable synchronizers:
- None
...
2.2 等待
public class Test {
public static void main(String[] args) throws Exception {
Thread1 t = new Thread1();
t.start();
t.join();
}
static class Thread1 extends Thread {
@Override
public void run() {
System.out.println("start");
try {
Thread.sleep(100000);
} catch (InterruptedException e) {}
System.out.println("end");
}
}
}
同樣通過(guò)線程dump可以看到主線程處于WAITING狀態(tài)
,子線程處于TIMED_WAITING
狀態(tài):
...
"Thread-0" #11 prio=5 os_prio=0 tid=0x0000000020bf7000 nid=0x4f94 waiting on condition [0x000000002189f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at Test$Thread1.run(Test.java:13)
Locked ownable synchronizers:
- None
...
"main" #1 prio=5 os_prio=0 tid=0x0000000004f63800 nid=0x431c in Object.wait() [0x0000000004eef000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076b6e0898> (a Test$Thread1)
at java.lang.Thread.join(Unknown Source)
- locked <0x000000076b6e0898> (a Test$Thread1)
at java.lang.Thread.join(Unknown Source)
at Test.main(Test.java:5)
Locked ownable synchronizers:
- None
...
下面演示wait方法導(dǎo)致的等待狀態(tài):
public class Test {
public static int i = 0;
public static void main(String[] args) throws Exception {
Thread1 t = new Thread1();
t.start();
synchronized (t) {
System.out.println("等待子線程");
t.wait();
}
System.out.println("主線程結(jié)束");
}
static class Thread1 extends Thread {
@Override
public void run() {
synchronized (this) {
for (int i = 0; i < 10; i++) {
try {
System.out.println(i);
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
notify();
}
}
}
}
通過(guò)線程堆棧觀察撬槽,主線程同樣處于等待WAITING狀態(tài):
...
"main" #1 prio=5 os_prio=0 tid=0x0000000005983800 nid=0xb54 in Object.wait() [0x00000000058df000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076b6e0aa8> (a Test$Thread1)
at java.lang.Object.wait(Unknown Source)
at Test.main(Test.java:8)
- locked <0x000000076b6e0aa8> (a Test$Thread1)
Locked ownable synchronizers:
- None
...
2.3 阻塞
public class Test {
public static void main(String[] args) throws Exception {
Thread1 t = new Thread1();
t.start();
test();
}
static class Thread1 extends Thread {
@Override
public void run() {
test();
}
}
static synchronized void test() {
System.out.println(Thread.currentThread().getName() + " -- start");
try {
Thread.sleep(100000);
} catch (InterruptedException e) {}
System.out.println(Thread.currentThread().getName() + " -- end");
}
}
通過(guò)線程dump可以看到子線程處于阻塞(BLOCKED)狀態(tài)此改,主線程處于超時(shí)等待(TIMED_WAITING)狀態(tài):
...
"Thread-0" #11 prio=5 os_prio=0 tid=0x0000000020ef1800 nid=0x4df4 waiting for monitor entry [0x0000000021b9f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at Test.test(Test.java:16)
- waiting to lock <0x000000076b6dea88> (a java.lang.Class for Test)
at Test$Thread1.run(Test.java:11)
Locked ownable synchronizers:
- None
...
"main" #1 prio=5 os_prio=0 tid=0x00000000051e3800 nid=0x3ee8 waiting on condition [0x000000000517f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at Test.test(Test.java:18)
- locked <0x000000076b6dea88> (a java.lang.Class for Test)
at Test.main(Test.java:5)
Locked ownable synchronizers:
- None
...
三、幾種方法的對(duì)比
- Thead.sleep(long millis):一定是當(dāng)前線程調(diào)用此方法侄柔,當(dāng)前線程進(jìn)入TIMED_WAITING狀態(tài)带斑,但不釋放對(duì)象鎖,millis后線程自動(dòng)蘇醒進(jìn)入就緒狀態(tài)勋拟。作用:給其它線程執(zhí)行機(jī)會(huì)的最佳方式勋磕。
- Thread.yield():一定是當(dāng)前線程調(diào)用此方法,當(dāng)前線程放棄獲取的CPU時(shí)間片敢靡,但不釋放鎖資源挂滓,由運(yùn)行狀態(tài)變?yōu)榫途w狀態(tài),讓OS再次選擇線程啸胧。作用:讓相同優(yōu)先級(jí)的線程輪流執(zhí)行赶站,但并不保證一定會(huì)輪流執(zhí)行。實(shí)際中無(wú)法保證yield()達(dá)到讓步目的纺念,因?yàn)樽尣降木€程還有可能被線程調(diào)度程序再次選中贝椿。Thread.yield()不會(huì)導(dǎo)致阻塞。該方法與sleep()類似陷谱,只是不能由用戶指定暫停多長(zhǎng)時(shí)間烙博。
- obj.join()/obj.join(long millis):當(dāng)前線程里調(diào)用其它線程T的join方法,當(dāng)前線程進(jìn)入WAITING/TIMED_WAITING狀態(tài)烟逊,當(dāng)前線程不會(huì)釋放已經(jīng)持有的對(duì)象鎖渣窜。線程T執(zhí)行完畢或者millis時(shí)間到,當(dāng)前線程一般情況下進(jìn)入RUNNABLE狀態(tài)宪躯,也有可能進(jìn)入BLOCKED狀態(tài)(因?yàn)閖oin是基于wait實(shí)現(xiàn)的)乔宿。
- obj.wait()/obj.wait(long millis):當(dāng)前線程調(diào)用對(duì)象的wait()方法,當(dāng)前線程釋放對(duì)象鎖访雪,進(jìn)入等待隊(duì)列详瑞。依靠notify()/notifyAll()喚醒或者wait(long timeout) timeout時(shí)間到自動(dòng)喚醒。
- obj.notify():?jiǎn)拘言诖藢?duì)象監(jiān)視器上等待的單個(gè)線程臣缀,選擇是任意性的坝橡。notifyAll()喚醒在此對(duì)象監(jiān)視器上等待的所有線程。notify肝陪,notifyAll和wait一起使用驳庭,用于協(xié)調(diào)多個(gè)線程對(duì)共享數(shù)據(jù)的存取刑顺,所以必須在synchronized語(yǔ)句塊內(nèi)使用氯窍,也就是說(shuō)饲常,調(diào)用wait(),notify()和notifyAll()的任務(wù)在調(diào)用這些方法前必須擁有對(duì)象的鎖狼讨。
- LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines):當(dāng)前線程進(jìn)入WAITING/TIMED_WAITING狀態(tài)贝淤。對(duì)比wait方法,不需要獲得鎖就可以讓線程進(jìn)入WAITING/TIMED_WAITING狀態(tài),需要通過(guò)LockSupport.unpark(Thread thread)喚醒政供。
參考:
Java線程的6種狀態(tài)及切換:https://blog.csdn.net/pange1991/article/details/53860651