package com.exam.test;
/**
* 懶漢式單例
* Created by xiangyang.laixiang on 2016/8/23.
*/
public class SingleInstance {
private static SingleInstance singleInstance;
/**
* 懶漢式單例,非線程安全
* @return
*/
public static SingleInstance getSingleInstance(){
if(singleInstance == null)
{
singleInstance = new SingleInstance();
}
return singleInstance;
}
/**
* 懶漢式單例,線程安全
* 這是實現線程安全最簡單的方式,但這里會導致一個性能問題,因為我們把整個getInstance同步起來了,
* 這就會導致每時每刻都只能有一個線程調用getInstance方法,而同步只發生在第一次聲明的時候
* 這就引出了雙重檢驗鎖的概念
* @return
*/
public synchronized static SingleInstance getSingleInstance2(){
if(singleInstance == null)
{
singleInstance = new SingleInstance();
}
return singleInstance;
}
}
/**
* 雙重檢驗鎖
*/
class DoubleCheckSingleInstance{
//使用volatile關鍵字修飾
//private static DoubleCheckSingleInstance singleInstance;
private static DoubleCheckSingleInstance singleInstance;
public static DoubleCheckSingleInstance getSingleInstance()
{
if(singleInstance == null)
{
synchronized (DoubleCheckSingleInstance.class)
{
if(singleInstance == null)
{
singleInstance = new DoubleCheckSingleInstance();
}
}
}
return singleInstance;
}
/**
* 上述這段代碼看起來很完美,但是中間存在著一個問題那就是 new DoubleCheckSingleInstance()
* 這個操作不是原子操作,大致有三步構成。
* 1. 給instance分配內存
* 2. 調用DoubleCheckSingleInstance構造函數初始化成員變量
* 3. 將instance指向內存
* 問題就出在這一部分,jvm即時編譯器中存在著指令重排序的優化。如果1,2,3的執行步驟不會存在問題
* 倘若1,3,2
* 那么當線程二執行完第三步以后,線程三搶占cpu資源,則進行檢查,發現instance不為空,則返回,但此時
* 成員變量尚未初始化,則會發生調用錯誤。
* 解決方案是使用volatile來修飾singleInstance來強制每次都從內存映像中讀取數據,其實volitile也可以起到
* 禁止重排序的功能,在java1.5以后使用比較安全。(1.5之前的內存模型是存在問題的)
*/
}
/**
* effective java中推薦的內部類實現單例的寫法 。
*/
class SingtonInstance{
private SingtonInstance()
{
System.out.println("constructor");
}
private static class SingtonHolder{
private static SingtonInstance instance = new SingtonInstance();
}
public static void beforeInvoke()
{
System.out.println("before invoke");
}
public static SingtonInstance getInstance()
{
/**
* 此處最開始理解有點繞,但是經過我對象的一個提醒恍然明白,對于內部類的所有對象對于其
* 外部類來說都是可見的,簡直是霸氣,否則private的可見范圍是不允許直接訪問的。
*/
return SingtonHolder.instance;
}
}
public class Test {
public static void main(String[] args) {
SingtonInstance.beforeInvoke();
SingtonInstance.getInstance().beforeInvoke();
}
}
單例模式(懶漢模式)
最后編輯于 :
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
推薦閱讀更多精彩內容
- 當我們使用單例模式,獲取單例的時候經常見到下面這種寫法: 為什么會這么寫呢,原因是為了避免多線程并發的時候創建多余...
- 單例模式4:多線程二(雙重鎖定)這種雙重鎖定考慮了線程安全,是正規寫法 游戲常用設計模式之單例設計模式的寫法大概常...