suspend和resume的廢除
查閱java api 可以發現suspend和resume已經被標註爲廢棄方法。原因其實也不是很複雜,因爲suspend在導致線程暫停的同時不會去釋放任何資源,直到在對應線程上執行了resume操作,被掛起的線程才能繼續,從而其他所有阻塞在相關鎖的線程也可以繼續執行,但是,如果resume操作意外的在suspend操作前就執行了,那麼被掛起的線程很難再去執行。
爲了理解suspend的這個問題,可以參考下面這個代碼demo
public class BadSuspend {
public static Object u = new Object();
static ChangeObjectThread t1= new ChangeObjectThread("t1");
static ChangeObjectThread t2= new ChangeObjectThread("t2");
public static class ChangeObjectThread extends Thread{
public ChangeObjectThread(String name){
super.setName(name);
}
@Override
public void run(){
synchronized(u){
System.out.println("in "+getName());
Thread.currentThread().suspend();
//ide這裏會將suspend方法畫上斜線表明已經廢棄使用了
}
}
}
public static void main(String[] args) throws InterruptedException {
t1.start();
Thread.sleep(1000);
t2.start();
t1.resume();
t2.resume();
t1.join();
t2.join();
}
}
運行結果:
in t1
in t2
但是要注意一點,此時我們的程序並沒有結束運行而是一直處於運行狀態!!
現在我們可以藉助jps和jstack工具來查看原因哦!
首先筆者用的系統是ubuntu18,當然Windows你就打開cmd就行。下面介紹ubuntu查看方式,windows cmd輸入的一樣。
打開終端,
輸入jps
jet@jet-X555LF:~$ jps
9124 Launcher
9126 BadSuspend
2343 Main
9336 Jps
jet@jet-X555LF:~
找到對應的pid,這裏的pid是9126
然後輸入jstack 9126
找到這一行
"t2" #11 prio=5 os_prio=0 tid=0x00007f4004209000 nid=0x23cd runnable [0x00007f3ff071c000]
java.lang.Thread.State: RUNNABLE
at java.lang.Thread.suspend0(Native Method)
at java.lang.Thread.suspend(Thread.java:1032)
at BadSuspend$ChangeObjectThread.run(BadSuspend.java:14)
- locked <0x00000000d70ad418> (a java.lang.Object)
這時要注意,當前線程t2是掛起的,但是它的線程狀態確實是RUNNABLE,這很有可能使我們誤判當前系統狀態。同時,雖然主函數中已經調用了resume(),但是由於時間先後順序的緣故,那個resume並沒有生效。
wait()和notify()實現可靠的suspend和resume
public class GoodSuspend {
public static Object u = new Object();
public static class ChangeObjectThread extends Thread {
volatile boolean suspendme = false;
public void suspendMe() {
suspendme = true;
}
public void resumeMe() {
suspendme = false;
synchronized (this) {
notify();
}
}
@Override
public void run() {
while (true) {
synchronized (this) {
while (suspendme) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized (u) {
System.out.println("in ChangeObjectThread");
}
Thread.yield();
}
}
}
public static class ReadObjectThread extends Thread{
@Override
public void run(){
while(true){
synchronized (u){
System.out.println("in ReadObjectThread");
}
Thread.yield();
}
}
}
public static void main(String[] args) throws InterruptedException {
ChangeObjectThread t1 = new ChangeObjectThread();
ReadObjectThread t2 = new ReadObjectThread();
t1.start();
t2.start();
Thread.sleep(1000);
t1.resumeMe();
System.out.println("suspend t1 2 sec");
Thread.sleep(2000);
System.out.println("resume t1");
t1.resumeMe();
}
}
代碼中,給出一個標記變量suspendme,表示當前線程是否被掛起。同時,增加了suspendMe()和resumeMe()兩個方法,分別用於掛起線程和繼續執行線程。