From:Java并發編程的藝術
- 目錄
BiBi - 并發編程 -0- 開篇
BiBi - 并發編程 -1- 挑戰
BiBi - 并發編程 -2- volatile
BiBi - 并發編程 -3- 鎖
BiBi - 并發編程 -4- 原子操作
BiBi - 并發編程 -5- Java內存模型
BiBi - 并發編程 -6- final關鍵字
BiBi - 并發編程 -7- DCL
BiBi - 并發編程 -8- 線程
BiBi - 并發編程 -9- ReentrantLock
BiBi - 并發編程 -10- 隊列同步器
BiBi - 并發編程 -11- 并發容器
BiBi - 并發編程 -12- Fork/Join框架
BiBi - 并發編程 -13- 并發工具類
BiBi - 并發編程 -14- 線程池
BiBi - 并發編程 -15- Executor框架
DCL實現單例
public class Singleton {
private volatile Singleton singleton;
// 構造函數是private,防止外部實例化
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) { // 第一次檢查
synchronized (Singleton.class) {
if (singleton == null) { // 第二次檢查,"double check"的由來
singleton = new Singleton();
}
}
}
return singleton;
}
}
Java中創建一個對象分為三個步驟:
1)在內存中開辟一塊地址
2)對象初始化
3)將指針指向這塊內存地址
Java中存在指令重排序現象。如果在新建Singleton對象的時候第2步和第3步發生了重排序,線程1將singleton指針指向了內存中的地址,但是此時我們的對象還沒有初始化。這個時候線程2進來,看到singleton不是null,于是直接返回。這個時候錯誤就發生了,線程2拿到了一個沒有經過初始化的對象。解決這個問題的思路也很簡單,就是使用volatile防止指令重排序。
內部類實現單例
public class Singleton {
private Singleton(){}
public static Singleton getInstance() {
return SingletonHolder.singleton;
}
private static class SingletonHolder {
private static Singleton singleton = new Singleton();
}
}
基于ClassLoader的實現多線程同步,即利用classloder的機制來保證初始化instance時只有一個線程。JVM在類初始化階段會獲取一個鎖,這個鎖可以同步多個線程對同一個類的初始化。
枚舉實現單例
public enum Singleton {
instance;
private Singleton(){}
public void doSomething(){}
}
優點:線程安全、保證只有一個實例、能夠實現反序列化。
其它單例實現方式在反序列化時會通過【反射】去重新創建對象。若要避免需要手動實現private Object readResolve(){return singleton;}