Java併發編程之常見面試題(二)

21.synchronized使用有哪些注意點?

  • 鎖對象不能爲空,因爲鎖是保存在對象頭中,對象都沒有,就沒有對象頭
  • 作用域不能過大,把所有代碼都被修飾,就是串行了,會影響到程序執行的速度
  • 避免死鎖,兩個鎖,兩個線程,線程一持有鎖一請求鎖二,線程二持有鎖二請求鎖一

22.如何選擇Lock和synchronized關鍵字?

  • 如何可以的話,不使用這兩個,儘量使用JUC包中的類
  • 優先使用synchronized,可以少寫代碼
  • 特別需要Lock的特性的時候才使用Lock,比如:靈活加鎖釋放鎖機制

23.多線程訪問同步方法的各種具體情況

  1. 兩個線程同時訪問一個對象的同步方法:會發生同步,鎖對象都爲同一個實例對象;
  2. 兩個線程同時訪問兩個對象的同步方法:互不影響,鎖對象不同;
  3. 兩個線程訪問的是synchronized的靜態方法:會發生同步,鎖對象都爲Class對象,Class對象只有一個;
  4. 同時訪問同步方法和非同步方法:非同步方法不受影響,不發生同步;
  5. 訪問同一個對象不同的普通同步方法:會發生同步,鎖對象默認爲同一個實例對象;
  6. 同時訪問靜態synchronized和非靜態synchronized方法:互不影響,靜態syn方法的鎖對象爲Class對象,非靜態syn方法的鎖對象爲一個實例對象this,實例對象和Class對象不是同一個對象,實例對象在堆中,Class對象在方法區中;
  7. 方法拋出異常後,會釋放鎖

24.多個線程等待同一個sync鎖的時候,JVM如何選擇獲取鎖的下一個線程?用的什麼算法呢?

處於不公平的狀態,和JVM的版本和具體實現都有關係,判定是隨機的。

25.sync使得同時只有一個線程執行代碼,性能較差,有什麼辦法可以提升性能?

  • 優化使用範圍,要被保護的區域,滿足業務需求的情況下,儘可能的小
  • 使用其它類型的Lock,比如讀寫鎖,讀不用線程安全,寫的時候保證線程安全

26.我想靈活的控制鎖的獲取和釋放,而現在釋放鎖的時機都被規定死了,怎麼辦?

自己實現Lock的接口

27.一句話總結synchronized?

JVM通過使用monitor,加減monitor計數器來加鎖和解鎖,保證了同時只有一個線程可以執行指定代碼,從而保證了線程安全,同時具有可重入和不可中斷的性質。

28.什麼是鎖的升級和降級?什麼是JVM裏的輕量級鎖,重量級鎖,偏斜鎖?

JVM利用關鍵字使用的次數,來對鎖進行優化,升級和降級:把輕量級鎖升級到重量級鎖等等

29.單例模式的作用及使用場景?

作用:

  1. 節省內存和計算:如果構造函數中有許多資源需要初始化,每次new都需要初始化,十分耗時,單例只需初始化一次;
  2. 保證結果正確:多線程進行統計操作時,需要一個全局的計數器,單例模式可保證計數器只有一個;
  3. 方便管理:一些常見的工具類可以用單例模式管理

使用場景:

  1. 工具類:比如日誌工具類,格式轉換工具類等,不管在哪裏使用,都是無狀態的,只需要一個實例對象即可;
  2. 全局信息類:比如全局的計數器,統計網站的全局訪問次數;

30.單例模式的各種寫法?

/**
 * 餓漢式
 */
public class HungerSingleton {

    private static final HungerSingleton instance = new HungerSingleton();

    private HungerSingleton() {}

    public static HungerSingleton getInstance() {
        return instance;
    }
}
/**
 * 懶漢式,雙重檢查鎖,防止反射
 */
public class DoubleCheckSingleton {

    private static volatile DoubleCheckSingleton instance;

    private DoubleCheckSingleton() {
        if (instance != null) {
            throw new RuntimeException("不允許重複創建實例");
        }
    }

    public DoubleCheckSingleton getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckSingleton.class) {
                if (instance == null) {
                    instance = new DoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}

/**
 * 餓漢式,靜態內部類,防止反射
 */
public class StaticInnerClassSingleton {

    private StaticInnerClassSingleton() {
        if (StaticInnerClass.instance != null) {
            throw new RuntimeException("不允許重複創建實例");
        }
    }

    public StaticInnerClassSingleton getInstance() {
        return StaticInnerClass.instance;
    }

    private static final class StaticInnerClass {
        private static final StaticInnerClassSingleton instance = new StaticInnerClassSingleton();
    }
}

31.哪種單例模式最好?

枚舉最好,寫法簡單,延遲加載,線程安全,防止反射和序列化

32.餓漢式、懶漢式各有什麼缺點?

餓漢式:一開始就加載,浪費內存,同時如果需要配置文件配置話,沒法去動態調整加載的內容;
懶漢式:寫法複雜,線程不安全;

33.爲什麼用雙重檢查鎖?

線程安全,延遲加載,volatile關鍵字可保證創建對象的時候能禁止重排序

34.什麼是Java內存模型?

  • 起因:不同CPU性能不同,不用JMM的話,需要我們手動同步;
  • 定義:一組規範,規範CPU和代碼之間一系列的轉換關係;
  • 重排序;
  • 可見性:主內存和本地內存,happens-before原則,volatile:禁止重排序,可見性,近朱者赤,輕量級的synchronized,適用於賦值操作,沒有判斷和累加操作;
  • 原子性:synchronized:加鎖和釋放鎖原理,近朱者赤;原子:基本類型賦值,引用賦值,原子包;

35.什麼是原子操作,Java中有哪些原子操作,生成對象的過程是不是原子操作?

  • 一系列的操作,要麼成功,要麼都失敗;
  • 基本類型和引用賦值,原子包;
  • 生成對象:創建空對象,調用構造方法,引用賦值,不是原子操作;

36.什麼是內存可見性?

在這裏插入圖片描述

37.64位的double和long寫入的時候是原子的嗎?

32位JVM上有可能出現前32和後32錯位的情況,64位JVM是原子的,商用JVM不需要。

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