21.synchronized使用有哪些注意點?
- 鎖對象不能爲空,因爲鎖是保存在對象頭中,對象都沒有,就沒有對象頭
- 作用域不能過大,把所有代碼都被修飾,就是串行了,會影響到程序執行的速度
- 避免死鎖,兩個鎖,兩個線程,線程一持有鎖一請求鎖二,線程二持有鎖二請求鎖一
22.如何選擇Lock和synchronized關鍵字?
- 如何可以的話,不使用這兩個,儘量使用JUC包中的類
- 優先使用synchronized,可以少寫代碼
- 特別需要Lock的特性的時候才使用Lock,比如:靈活加鎖釋放鎖機制
23.多線程訪問同步方法的各種具體情況
- 兩個線程同時訪問一個對象的同步方法:會發生同步,鎖對象都爲同一個實例對象;
- 兩個線程同時訪問兩個對象的同步方法:互不影響,鎖對象不同;
- 兩個線程訪問的是synchronized的靜態方法:會發生同步,鎖對象都爲Class對象,Class對象只有一個;
- 同時訪問同步方法和非同步方法:非同步方法不受影響,不發生同步;
- 訪問同一個對象不同的普通同步方法:會發生同步,鎖對象默認爲同一個實例對象;
- 同時訪問靜態synchronized和非靜態synchronized方法:互不影響,靜態syn方法的鎖對象爲Class對象,非靜態syn方法的鎖對象爲一個實例對象this,實例對象和Class對象不是同一個對象,實例對象在堆中,Class對象在方法區中;
- 方法拋出異常後,會釋放鎖
24.多個線程等待同一個sync鎖的時候,JVM如何選擇獲取鎖的下一個線程?用的什麼算法呢?
處於不公平的狀態,和JVM的版本和具體實現都有關係,判定是隨機的。
25.sync使得同時只有一個線程執行代碼,性能較差,有什麼辦法可以提升性能?
- 優化使用範圍,要被保護的區域,滿足業務需求的情況下,儘可能的小
- 使用其它類型的Lock,比如讀寫鎖,讀不用線程安全,寫的時候保證線程安全
26.我想靈活的控制鎖的獲取和釋放,而現在釋放鎖的時機都被規定死了,怎麼辦?
自己實現Lock的接口
27.一句話總結synchronized?
JVM通過使用monitor,加減monitor計數器來加鎖和解鎖,保證了同時只有一個線程可以執行指定代碼,從而保證了線程安全,同時具有可重入和不可中斷的性質。
28.什麼是鎖的升級和降級?什麼是JVM裏的輕量級鎖,重量級鎖,偏斜鎖?
JVM利用關鍵字使用的次數,來對鎖進行優化,升級和降級:把輕量級鎖升級到重量級鎖等等
29.單例模式的作用及使用場景?
作用:
- 節省內存和計算:如果構造函數中有許多資源需要初始化,每次new都需要初始化,十分耗時,單例只需初始化一次;
- 保證結果正確:多線程進行統計操作時,需要一個全局的計數器,單例模式可保證計數器只有一個;
- 方便管理:一些常見的工具類可以用單例模式管理
使用場景:
- 工具類:比如日誌工具類,格式轉換工具類等,不管在哪裏使用,都是無狀態的,只需要一個實例對象即可;
- 全局信息類:比如全局的計數器,統計網站的全局訪問次數;
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不需要。