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