利用notify和wait方法實現suspend和resume(附帶jstack和jps查看線程運行狀態)

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()兩個方法,分別用於掛起線程和繼續執行線程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章