天天看點

4.2 synchronized補充

一、其他鎖的概念(這個其他相對于第4節而言)

1. 鎖的粗化

public class LockAppend {
    Object object1 = new Object();
    public void method() {
        synchronized (object1) {
            System.out.println("No1");
        }

        synchronized (object1) {
            System.out.println("No2");
        }

        synchronized (object1) {
            System.out.println("No3");
        }
    }
}      

對于一個方法而言, 裡面加了三把鎖, 這樣是沒有任何意義的. 是以可以将其進行粗化處理

public class LockAppend {
    Object object1 = new Object();
    public void method() {
        synchronized (object1) {
            System.out.println("No1");
            System.out.println("No2");
            System.out.println("No3");
        }
    }
}      

2. 鎖的消除

public class LockAppend {
    public void method1(){
        Object object2 = new Object();
        
        synchronized (object2) {
            // ...
            System.out.println("");
        }
    }
}      

在方法裡這樣加鎖是沒有任何意義的.

因為, object2這個變量沒有線程都是獨有的, 不可能被其他線程通路到. 這樣加鎖沒有任何一意義, 是以把鎖去掉, 也就是消除.

 3. 從jvm角度出發, 對Synchronized的優化

  1. 鎖的膨脹更新, 這是一個不可逆的過程. 就是從無鎖--->偏向鎖--->輕量級鎖--->重量級鎖 
  2. 鎖的粗化
  3. 鎖的消除
  4. 自旋鎖

二. 線程的逃逸分析

使用逃逸分析,編譯器可以對代碼做如下優化:

一、同步省略。如果一個對象被發現隻能從一個線程被通路到,那麼對于這個對象的操作可以不考慮同步。

二、将堆配置設定轉化為棧配置設定。如果一個對象在子程式中被配置設定,要使指向該對象的指針永遠不會逃逸,對象可能是棧配置設定的候選,而不是堆配置設定。

三、分離對象或标量替換。有的對象可能不需要作為一個連續的記憶體結構存在也可以被通路到,那麼對象的部分(或全部)可以不存儲在記憶體,而是存儲在CPU寄存器中。

是不是所有的對象和數組都會在堆記憶體配置設定空間?

不一定

在Java代碼運作時,通過JVM參數可指定是否開啟逃逸分析, -XX:+DoEscapeAnalysis : 表示開啟逃逸分析 -XX:-DoEscapeAnalysis : 表示關閉逃逸分析。從jdk 1.7開始已經預設開啟逃逸分析,如需關閉,需要指定-XX:-DoEscapeAnalysis

關于逃逸分析的案例論證見Git課程源碼

public class T0_ObjectStackAlloc {
    /**
     * 進行兩種測試
     * 關閉逃逸分析,同時調大堆空間,避免堆内GC的發生,如果有GC資訊将會被列印出來
     * VM運作參數:-Xmx4G -Xms4G -XX:-DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
     *
     * 開啟逃逸分析
     * VM運作參數:-Xmx4G -Xms4G -XX:+DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
     *
     * 執行main方法後
     * jps 檢視程序
     * jmap -histo 程序ID
     *
     */
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 500000; i++) {
            alloc();
        }
        long end = System.currentTimeMillis();
        //檢視執行時間
        System.out.println("cost-time " + (end - start) + " ms");
        try {
            Thread.sleep(100000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
    }
 
    private static TulingStudent alloc() {
        //Jit對編譯時會對代碼進行 逃逸分析
        //并不是所有對象存放在堆區,有的一部分存線上程棧空間
        TulingStudent student = new TulingStudent();
        return student;
    }
 
    static class TulingStudent {
        private String name;
        private int age;
    }
}      

as

4.2 synchronized補充