1. 程式人生 > >單例模式的四種執行緒安全的實現

單例模式的四種執行緒安全的實現

1.描述:

    Singleton(單例)是設計模式的一種,為了保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。

2.主要特點:

    1)單例類確保自己只有一個例項(建構函式私有:不被外部例項化,也不被繼承)。

    2)單例類必須自己建立自己的例項。

    3)單例類必須為其他物件提供唯一的例項。

3.單例模式的應用:

    資源管理器,回收站,印表機資源,執行緒池,快取,配置資訊類,管理類,控制類,門面類,代理類通常被設計為單例類

    如果程式有多個類載入器又同時使用單例模式就有可能多個單例並存就要找相應解決方法了

4.實現方法:

如果應用程式總是建立並使用單例例項或在建立和執行時開銷不大。

1).Eager initialization 餓漢式單例類(依賴jvm在載入類時建立唯一單例例項)

[java] view plain copy print?
  1. publicclass EagerSingleton {    
  2.         // jvm保證在任何執行緒訪問uniqueInstance靜態變數之前一定先建立了此例項  
  3.         privatestatic EagerSingleton uniqueInstance = new EagerSingleton();    
  4.         // 私有的預設構造子,保證外界無法直接例項化  
  5.         private EagerSingleton() {    
  6.         }    
  7.         // 提供全域性訪問點獲取唯一的例項  
  8.         publicstatic EagerSingleton getInstance() {    
  9.                 return uniqueInstance;    
  10.         }    
  11. }  
public class EagerSingleton {  
        // jvm保證在任何執行緒訪問uniqueInstance靜態變數之前一定先建立了此例項  
        private static EagerSingleton uniqueInstance = new EagerSingleton();  

        // 私有的預設構造子,保證外界無法直接例項化  
        private EagerSingleton() {  
        }  

        // 提供全域性訪問點獲取唯一的例項  
        public static EagerSingleton getInstance() {  
                return uniqueInstance;  
        }  
}
如果開銷比較大,希望用到時才建立就要考慮延遲例項化,或者Singleton的初始化需要某些外部資源(比如網路或儲存裝置),就要用後面的方法了.

2)Lazy initialization 懶漢式單例類

[java] view plain copy print?
  1. publicclass LazySingleton {    
  2.         privatestatic LazySingleton uniqueInstance;    
  3.         private LazySingleton() {    
  4.         }    
  5.         publicstaticsynchronized LazySingleton getInstance() {    
  6.                 if (uniqueInstance == null)    
  7.                         uniqueInstance = new LazySingleton();    
  8.                 return uniqueInstance;    
  9.         }    
  10. }   
public class LazySingleton {  
        private static LazySingleton uniqueInstance;  

        private LazySingleton() {  
        }  

        public static synchronized LazySingleton getInstance() {  
                if (uniqueInstance == null)  
                        uniqueInstance = new LazySingleton();  
                return uniqueInstance;  
        }  
} 
同步一個方法可能造成程式執行效率下降100倍,完全沒有必要每次呼叫getInstance都加鎖,事實上我們只想保證一次初始化成功,其餘的快速返回而已,如果在getInstance頻繁使用的地方就要考慮重新優化了.

3)”雙檢鎖”(Double-Checked Lock)儘量將”加鎖”推遲,只在需要時”加鎖”(僅適用於Java 5.0 以上版本,volatile保證原子操作) 
happens-before:”什麼什麼一定在什麼什麼之前執行”,也就是保證順序性.
現在的CPU有亂序執行的能力(也就是指令會亂序或並行執行,可以不按我們寫程式碼的順序執行記憶體的存取過程),並且多個CPU之間的快取也不保證實時同步,只有上面的happens-before所規定的情況下才保證順序性.

JVM能夠根據CPU的特性(CPU的多級快取系統、多核處理器等)適當的重新排序機器指令,使機器指令更符合CPU的執行特點,最大限度的發揮機器的效能.

如果沒有volatile修飾符則可能出現一個執行緒t1的B操作和另一執行緒t2的C操作之間對instance的讀寫沒有happens-before,可能會造成的現象是t1的B操作還沒有完全構造成功,但t2的C已經看到instance為非空,這樣t2就直接返回了未完全構造的instance的引用,t2想對instance進行操作就會出問題.

    volatile 的功能:
1. 避免編譯器將變數快取在暫存器裡  
2. 避免編譯器調整程式碼執行的順序

優化器在用到這個變數時必須每次都小心地重新讀取這個變數的值,而不是使用儲存在暫存器裡的備份。

[java] view plain copy print?
  1. publicclass DoubleCheckedLockingSingleton {    
  2.         // java中使用雙重檢查鎖定機制,由於Java編譯器和JIT的優化的原因系統無法保證我們期望的執行次序。  
  3.         // 在java5.0修改了記憶體模型,使用volatile宣告的變數可以強制遮蔽編譯器和JIT的優化工作  
  4.         privatevolatilestatic DoubleCheckedLockingSingleton uniqueInstance;    
  5.         private DoubleCheckedLockingSingleton() {    
  6.         }    
  7.         publicstatic DoubleCheckedLockingSingleton getInstance() {    
  8.                 if (uniqueInstance == null) {    
  9.                         synchronized (DoubleCheckedLockingSingleton.class) {    
  10.                                 if (uniqueInstance == null) {    
  11.                                         uniqueInstance = new DoubleCheckedLockingSingleton();    
  12.                                 }    
  13.                         }    
  14.                 }    
  15.                 return uniqueInstance;    
  16.         }    
  17. }    
public class DoubleCheckedLockingSingleton {  
        // java中使用雙重檢查鎖定機制,由於Java編譯器和JIT的優化的原因系統無法保證我們期望的執行次序。  
        // 在java5.0修改了記憶體模型,使用volatile宣告的變數可以強制遮蔽編譯器和JIT的優化工作  
        private volatile static DoubleCheckedLockingSingleton uniqueInstance;  

        private DoubleCheckedLockingSingleton() {  
        }  

        public static DoubleCheckedLockingSingleton getInstance() {  
                if (uniqueInstance == null) {  
                        synchronized (DoubleCheckedLockingSingleton.class) {  
                                if (uniqueInstance == null) {  
                                        uniqueInstance = new DoubleCheckedLockingSingleton();  
                                }  
                        }  
                }  
                return uniqueInstance;  
        }  
}  
4)Lazy initialization holder class 滿足所有 Double-Checked Locking 滿足的條件,並且沒有顯示的同步操作
[java] view plain copy print?
  1. publicclass LazyInitHolderSingleton {    
  2.         private LazyInitHolderSingleton() {    
  3.         }    
  4.         privatestaticclass SingletonHolder {    
  5.                 privatestaticfinal LazyInitHolderSingleton INSTANCE = new LazyInitHolderSingleton();    
  6.         }    
  7.         publicstatic LazyInitHolderSingleton getInstance() {    
  8.                 return SingletonHolder.INSTANCE;    
  9.         }    
  10. }    
public class LazyInitHolderSingleton {  
        private LazyInitHolderSingleton() {  
        }  

        private static class SingletonHolder {  
                private static final LazyInitHolderSingleton INSTANCE = new LazyInitHolderSingleton();  
        }  

        public static LazyInitHolderSingleton getInstance() {  
                return SingletonHolder.INSTANCE;  
        }  
}  
根據jvm規範,當某物件第一次呼叫LazyInitHolderSingleton.getInstance()時,LazyInitHolderSingleton類被首次主動使用,jvm對其進行初始化(此時並不會呼叫LazyInitHolderSingleton()構造方法),然後LazyInitHolderSingleton呼叫getInstance()方法,該方法中,又首次主動使用了SingletonHolder類,所以要對SingletonHolder類進行初始化,初始化中,INSTANCE常量被賦值時才呼叫了 LazyInitHolderSingleton的構造方法LazyInitHolderSingleton(),完成了例項化並返回該例項。當再有物件(也許是在別的執行緒中)再次呼叫LazyInitHolderSingleton.getInstance()時,因為已經初始化過了,不會再進行初始化步驟,所以直接返回INSTANCE常量即同一個LazyInitHolderSingleton例項。

相關推薦

spring模式中,執行安全問題

@RequestMapping(value = "getPsdbData", method = RequestMethod.POST) public Map<String, Object> getPsdbData(String key,HttpServletRequest reques

你的模式真的是執行安全的嗎?

在我們平時的專案中,單例模式是十分常見的設計模式,當然我們使用最多的是"懶漢式": public class SingleTon { private static SingleTon instance = null; private SingleTon(

java模式 原子類 執行安全

public class Singleton {private static AtomicReference<Singleton> singleton = new AtomicReference<>();private Singleton() {}p

singleton模式執行安全實現

1.描述:     Singleton(單例)是設計模式的一種,為了保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。 2.主要特點:     1)單例類確保自己只有一個例項(建構函式私有:不被外部例項化,也不被繼承)。     2)單例類必須自己建立自己

模式執行安全實現

1.描述:    Singleton(單例)是設計模式的一種,為了保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。2.主要特點:    1)單例類確保自己只有一個例項(建構函式私有:不被外部例項化,也不被繼承)。    2)單例類必須自己建立自己的例項。    3)單例類必須為其他物件提供唯一的例項

模式在多執行下的執行安全問題

/** * 雙重枷鎖 * java5以上版本才適用 * **/ class DoubleLockSingleton{ private volatile static DoubleLockSingleton doubleLockSingleton; private DoubleLockSi

第六章模式與多執行——立即載入“餓漢模式”與延遲載入“懶漢模式

立即載入就是使用類的時候已經將物件建立完畢了,也稱為“餓漢模式” package test01; public class MyObject { // 建立物件 private static MyObject object = new MyObject(); private MyObjec

Java多執行學習筆記21之模式與多執行

詳細程式碼見:github程式碼地址   第六章 單例模式與多執行緒 前言: 我之前已經開設了23個設計模式這個專欄,介紹了很多的Java設計模式,其中一些模式對於絕 大多數程式語言設計思想都是類似的,需要了解單例模式的可以去看看。 我們在實際開發中經常用到單例模式,但

c++模式,多執行使用

c++ 11保證了這樣做是執行緒安全的。 一:class Singleton{ static Singleton* GetInstance(){         static Singleton s;     

模式與多執行之間的關係總結

給大家推薦個靠譜的公眾號程式設計師探索之路,大家一起加油 單例模式與多執行緒之間的關係總結(魔怔多執行緒中~~~~~)   近日筆者被多執行緒與單例物件之間的關係產生了混淆。通過了一段時間的查閱,理清了兩者之間的管理,現做筆記梳理。如有不足,歡迎指出:) 在我在考慮考慮他們的時候思考了以

Java多執行核心技術(五)模式與多執行

本文只需要考慮一件事:如何使單例模式遇到多執行緒是安全的、正確的 1.立即載入 / "餓漢模式" 什麼是立即載入?立即載入就是使用類的時候已經將物件建立完畢,常見的實現辦法就是直接 new 例項化。 public class MyObject { private static MyObject m

再說說模式和多執行(靜態內部類實現

靜態內部類: package thread.singleton; public class StaticInnerClassSingleton { private static class Singleton{ private static Singleton si

Java模式及建立模式的多執行問題 volatile synchronized 關鍵字

接下來,說說我對多執行緒中volitile 和 synchronized的理解 這兩個關鍵字都是java內建的用於實現執行緒同步的機制,其中: volitile用於修飾變數,用於同步主記憶體和執行緒儲存中的變數值,但是volitile使用應牢記 —— 只有在狀態真正獨立

【Java 模式】Java 模式在多執行環境中可能存在的問題

在多執行緒環境下,使用延遲載入的方式實現單例模式,會出現錯誤。 例如,使用如下方式實現單例類: package study20170307; /** * Created by apple on 17/3/7. */ public class Sin

Java多執行-----模式在多執行中的使用用問題

   1.餓漢模式(立即載入模式)與多執行緒 不管需不需要用到例項都要去建立例項,即在類產生的時候就建立好例項 package com.thread; /** * 餓漢模式 * * @author yyx 2018年12月28日 */ public cl

寫一下單模式,考慮執行安全執行安全的情況

凡是提到設計模式,面試官很喜歡問最簡單的單例模式。 方法一 單例模式最簡單的寫法如下 public class SingletonPatternA { private static SingletonPatternA instance =

模式在多執行中的安全性研究

概述 關於一般單例模式的建立和分析在我的另一篇部落格《Java設計模式——單件模式》中有詳細說明。只是在上篇部落格中的單例是針對於單執行緒的操作,而對於多執行緒卻並不適用,本文就從單例模式與多執行緒安全的角度出發,講解單例模式在多執行緒中應該如何被使用。

餓漢式模式在多執行中併發訪問的解決方案

/**  * 懶漢式 如果在多執行緒重會出現執行緒安全問題   * */ class Sing {private static Sing sin = null;private Sing() {}// 通過同步鎖直接就可以解決執行緒安全問題 這裡屬於的是靜態的所以這裡的//

執行學習筆記(三)之模式中的執行問題

在某些情況下,每個類只需要一個例項,單例模式就是保證在整個應用程式的生命週期中,任何一個時刻,單例類的例項都只存在一個(當然也可以不存在),核心點: 將採用單例模式的類的構造方法私有化(private修飾) 在其內部產生該類的例項化物件,並將其封裝成priv

模式遇到多執行併發的時候

/*  * GZYY    2016-12-5  上午9:17:44  * author: zsz  */ public class Singleton {     private static Singleton singleton;     public static Singleton getInsta