關於join() 是否會釋放鎖的一些思考


# 首先從一個很有意思的問題開始:

  - 問 : Thread 的join() 方法是否會釋放鎖?

  - 答: 會!

# 如果一切到這里就結束了,那可能也就沒有這篇小記了,但是我的腦子卻冒出了一些奇怪的想法:

  - 釋放哪個對象的鎖呢?

  - 難道是釋放父線程所持有的所有對象的鎖?

  -- 其實如果看了源碼,很容易明白釋放的是運行(這個地方可能有些歧義,但是我也不知道怎么說最好)join()方法的那個線程對象的鎖,不過這些都是后話,我們且往下看;

# 然后我就寫了代碼來驗證一下我的猜想, 代碼如下:

public class QQ {

    public static void main(String[] args) {
        Object oo = new Object();

        Thread thread1 = new MyThread("thread1 -- ", oo);
        thread1.start();

        synchronized (oo) {
            for (int i = 0; i < 100; i++) {
                if (i == 20) {
                    try {
                        thread1.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + " -- " + i);
            }
        }
    }

}

class MyThread extends Thread {

    private String name;
    private Object oo;

    public MyThread(String name, Object oo) {
        this.name = name;
        this.oo = oo;
    }

    @Override
    public void run() {
        synchronized (oo) {
            for (int i = 0; i < 100; i++) {
                System.out.println(name + i);
            }
        }
    }

}

  - 運行一下,輸出到 main -- 19 的時候,卡住了。

  - 接下來我們來尋找卡住的原因;

  -- 先使用jps找到出問題程序的進程號

  -- jstack pid 來查看線程堆棧,結果如下圖

"Thread-1" #14 prio=5 os_prio=0 tid=0x0000000018fa9000 nid=0x3f80 waiting for monitor entry [0x0000000019b0f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        - waiting to lock <0x00000000d8a06298> (a java.lang.Object)


"main" #1 prio=5 os_prio=0 tid=0x000000000228e800 nid=0x3d6c in Object.wait() [0x00000000028af000]
   java.lang.Thread.State: WAITING (on object monitor)
        - locked <0x00000000d8a06298> (a java.lang.Object)

  -- 上圖中我刪掉了很多東西,只留下了一些關鍵的部分;首先我們看到 Thread-1 和 main 都在 waiting 狀態,然后再注意到 Thread-1 在等待鎖 <0x00000000d8a06298>,

但是main持有鎖<0x00000000d8a06298>, 這又是什么情況呢? 難道 main 沒有釋放鎖?

  - 這時候我們就回到了最初的問題,到底join()的時候釋放的是誰的鎖,通過查看join()方法的源碼,很容易看到,其實調用的是 this.wait(),也就是說釋放的是Thread-1 這個對象的鎖

# 接着我們來用下面的代碼證實一下我們得出的結論

public class QQ {

    public static void main(String[] args) {
        Object oo = new Object();

        Thread thread1 = new MyThread("thread1 -- ", oo);
        thread1.start();

        synchronized (thread1) {
            for (int i = 0; i < 100; i++) {
                if (i == 20) {
                    try {
                        thread1.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + " -- " + i);
            }
        }
    }

}

class MyThread extends Thread {

    private String name;
    private Object oo;

    public MyThread(String name, Object oo) {
        this.name = name;
        this.oo = oo;
    }

    @Override
    public void run() {
        synchronized (this) {
            for (int i = 0; i < 100; i++) {
                System.out.println(name + i);
            }
        }
    }

}

  - 很容易驗證我們的猜想和理解是正確的

# 再接下來我們看一下如果調用wait() 方法,應該是怎么個情況呢;

 

public class QQ {

    public static void main(String[] args) {
        Object oo = new Object();

        Thread thread1 = new MyThread("thread1 -- ", oo);
        thread1.start();

        synchronized (oo) {
            for (int i = 0; i < 100; i++) {
                if (i == 20) {
                    try {
                        oo.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + " -- " + i);
            }
        }
    }

}

class MyThread extends Thread {

    private String name;
    private Object oo;

    public MyThread(String name, Object oo) {
        this.name = name;
        this.oo = oo;
    }

    @Override
    public void run() {
        synchronized (oo) {
            for (int i = 0; i < 100; i++) {
                System.out.println(name + i);
            }
            oo.notifyAll();
        }
    }

}

  - 乍一看,和調用join() 方法的現象一樣; 

  - 嗯。。。有點意思了。。。

 

# 最后補充一點:jstack中的Thread-1 和 我們自己定義的 thread1 是不一樣的,如果想要在jstack中顯示我們自己定義的線程名, 則需要調用Thread的setName()方法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM