Java線程探究 ——synchronized問題——如果鎖住的對象被垃圾回收了,會發生什麼奇妙的事情

如果synchronized鎖住的對象被垃圾回收器回收了,會發生什麼呢?

下面代碼的目的是讓線程t1先進入synchronized(a)代碼塊,然後主線程調用full gc回收對象a,然後看看發生了什麼

public class Main {
    //定義類A
    public static class A{
        int i = 3;
    }
    //等會要實驗的靜態引用a
    public static A a = new A();
    public static void main(String[] args) throws InterruptedException {
        //實例化線程t1
        Thread t1 = new Thread() {
            @Override
            public void run()
            {
                synchronized(a)
                {
                    System.out.println("enter1");
                    //睡三秒好讓主線程gc
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("show a value");
                    //因爲a已經被垃圾回收,看看是否報錯
                    System.out.println(a.i);
                    System.out.println("leave1");
                }
            }
        };
        t1.start();
        //爲了先讓t1進如synchronized,所以讓主線程睡1s
        Thread.sleep(1000);
        //手動調用full gc 回收a
        a=null;
        System.gc();
        System.out.println("set a = null and do a full GC");
        //等待線程t1結束
        t1.join();
        System.out.println("finish");
    }
}

程序結果

enter1
set a = null and do a full GC
show a value
Exception in thread "Thread-0" java.lang.NullPointerException
    at normal.Main$1.run(Main.java:26)

finish

不出所料果然因爲a被回收,而訪問不了字段i!

再想想如果代碼塊鎖住的對象都被回收了,那代碼塊會不會在結束的時候報錯呢?

實驗如下

public class Main {
    //定義類A
    public static class A{}
    //等會要實驗的靜態引用a
    public static A a = new A();
    public static void main(String[] args) throws InterruptedException {
        //實例化線程t1
        Thread t1 = new Thread() {
            @Override
            public void run()
            {
                synchronized(a)
                {
                    System.out.println("enter1");
                    //睡三秒好讓主線程gc
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("show a value");
                    //因爲a已經被垃圾回收,看看是否報錯
                    System.out.println("leave1");
                }
            }
        };
        t1.start();
        //爲了先讓t1進如synchronized,所以讓主線程睡1s
        Thread.sleep(1000);
        //手動調用full gc 回收a
        a=null;
        System.gc();
        System.out.println("set a = null and do a full GC");
        //等待線程t1結束
        t1.join();
        System.out.println("finish");
    }
}

實驗結果

enter1
set a = null and do a full GC
show a value
leave1
finish

並沒有報錯!!!

我們來分析線程中run函數的的字節碼指令

         0: getstatic     #15                 // Field normal/Main.a:Lnormal/Mai
n$A;
         3: dup
         4: astore_1
         5: monitorenter
         6: getstatic     #21                 // Field java/lang/System.out:Ljav
a/io/PrintStream;
         9: ldc           #27                 // String enter1
        11: invokevirtual #29                 // Method java/io/PrintStream.prin
tln:(Ljava/lang/String;)V
        14: ldc2_w        #35                 // long 3000l
        17: invokestatic  #37                 // Method java/lang/Thread.sleep:(
J)V
        20: goto          28
        23: astore_2
        24: aload_2
        25: invokevirtual #41                 // Method java/lang/InterruptedExc
eption.printStackTrace:()V
        28: getstatic     #21                 // Field java/lang/System.out:Ljav
a/io/PrintStream;
        31: ldc           #46                 // String leave1
        33: invokevirtual #29                 // Method java/io/PrintStream.prin
tln:(Ljava/lang/String;)V
        36: aload_1

        37: monitorexit
我們知道monitorenter代表lock a,monitorexit代表unlock a(實際上是lock,unlock作用於操作棧頂,當然棧頂就是a),這說明什麼呢?

        說明monitorexit 指令不需要對象a在內存中存活,即使對象a被垃圾回收了,這條指令不會報錯,只會當作什麼都沒發生

那麼monitorenter需不需要作用的對象存活呢?

          把程序稍微改改就能驗證一下。我驗證出來的結果是——monitorenter必須保證作用的對象存活,否則會報出NullPointerException異常

 

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