最近看到一篇關於Java中instance的文章(http://www.zhihu.com/question/29971746),引發對單例模式的一些思考,並從網上搜集了一些關於Java單例模式的文章,總結如下:
首先,貼出三種單例設計的簡單代碼:
1.延遲加載——不考慮效率問題的延遲加載
public class SingleTon{ private static SingleTon instance = null; public static synchronized SingleTon getInstance(){ if(instance==null){ instance = new SingleTon(); } } }
2.即時加載的基本實現
public class SingleTon(){ private static SingleTon instance = new SingleTon(); public static SingleTon getInstance(){ return instance; } }
3.Double Check方式——關鍵考慮在於並發環境下返回結果的性能提升能否抵消多出來的兩次判斷跳轉
public class SingleTon{ private volatile static SingleTon uniqueInstance; private SingleTon(){} public static SingleTon getInstance(){ if(uniqueInstance==null){ synchronized(SingleTon.class){ if(uniqueInstance == null){ uniqueInstance = new SingleTon(); } } } return uniqueInstance; } }
分析:
1、第1和第2的方式,只考慮效率問題的話,若不是出現“單例爆炸”的情況(既在系統中存在了大量不同的實例,
使用即時加載,這些沒用的實例同時加載..造成效率低),延遲加載和即時加載效率上差異不大...
2、第3種DCL(Double Check Lock),兩次檢查instance==null,第一個為了避免每次都加鎖,避免了一定的加鎖的開銷;
第二個判斷為了避免同步的問題,such as:十個線程同時調用getInstance()方法,都執行了第一步檢查instance == null,並且其中一個線程成功獲得了鎖(執行了synchronized語句),接下來如果沒有再一次判斷instance == null,則十個線程將生成10對象,這違背了單例的初衷,其中DCL對開銷的關注在於:使用DoubleCheck方式的主要出發點是”避免同步鎖自身的初始化開銷”,而不是”避免被鎖住 內容的執行開銷“, 即需要考慮到加鎖的開銷和條件判斷的開銷哪個更大。。
另一個特點是變量instance的類型聲明中添加了volatile,因為像下面這種創建對象的語句並不是原子操作,volatile可以使其成為原子操作,避免同步問題
