/** * * @author Taowd * 功 能:單例模式,餓漢模式,線程安全,效率比較低 * 優勢:寫法簡單,線程安全 * 缺點:加載速度比較慢,某些特定狀況下會耗費內存 * 編寫時間:2017-5-11-上午8:37:37 */ public class Singleton2 { // 私有化構造參數 private Singleton2() { } // 將自身實力對象設置一個屬性,並加上static和final修飾符 private static final Singleton2 singleton2 = new Singleton2(); // 靜態方法返回該類的示例 public static Singleton2 getInstance() { return singleton2; } }
package com.taowd.singleton; /** * * @author Taowd * 功能:單例模式,懶漢模式(飽漢模式),非線程安全的 * 優勢:編寫簡單,類加載速度快,使用速度慢 * 缺點:併發環境下可能出現多個Singleton實例 * 編寫時間:2017-5-11-上午8:27:25 */ public class Singleton1 { // 把構造方法私有化,防止經過new Singleton() 去實例化 private Singleton1() { } // 定義一個Singleton類型的示例,不進行初始化,注意這裏沒有使用final關鍵字 private static Singleton1 singleton; // 定義一個靜態方法,外部調用時再初始化Singleton,可是多線程訪問時可能形成重複初始化的問題 public static Singleton1 getInstance() { if (singleton == null) { singleton = new Singleton1(); } return singleton; } }
package com.taowd.singleton; /** * * @author Taowd * 功能:最終版,最優方案 * 優勢:內存佔用低,效率高,線程安全,多線程操做原子性 * 編寫時間:2017-5-11-上午8:43:08 */ public class Singleton { // 把構造方法私有化,防止經過new Singleton() 去實例化 private Singleton() { } // 定義一個Singleton類型的示例,不進行初始化,注意這裏沒有使用final關鍵字 // volatile 保證了多線程訪問instance變量的可見性,避免了instance初始化時,其餘變量屬性還沒賦值完就被另外線程調用 // volatile: private static volatile Singleton instance; // 定義一個靜態方法,外部調用時再初始化Singleton,可是多線程訪問時可能形成重複初始化的問題 public static Singleton getInstance() { // 對象實例化與否判斷(不適用同步代碼,instance不等於null時,直接返回對象,提升執行效率) if (instance == null) { // 同步代碼塊(對象未初始化時,使用同步代碼,保證多線程訪問對象在第一次被建立後,再也不重複被建立) // synchronized: synchronized (Singleton.class) { // 未初始化,則初始化instance變量 if (instance == null) { instance = new Singleton(); } } } return instance; } }
package com.taowd.test; import org.junit.Test; import com.taowd.singleton.Singleton1; public class SingletonTest { @Test public void TestSingleton() { Singleton1 singleton1 = Singleton1.getInstance(); Singleton1 singleton2 = Singleton1.getInstance(); System.out.println(singleton1 == singleton2 ? "同一對象" : "不是同一對象"); } }
之前對單例模式不是很清楚,經過這幾個例子也學習了一下單例模式,其中對volatile關鍵字的含義的不是很明白,由於不多用到這個關鍵字,查閱了一下資料下面將個人查閱的信息簡單說明一下。緩存
volatile關鍵字能夠保證可見性和執行順序,可見性既是先發生的原子修改操做必定會在讀操做以前執行完成(同一時間對volatile變量只能有一個操做);執行順序的含義既是volatile關鍵字會阻止編譯器或JVM對代碼重排序,或把volatile變量從同步區域(synchronized方法,synchronized代碼塊..)中移除出來。安全
Java的內存模型(JMM)能夠保證一個線程修改了某個變量,該變量的改變對另外一個線程是可見的,也就是說另外一個線程取得的這個變量的值,必定是前一個線程修改以後的值。這個也被稱做"happens-before" 。多線程
volatile關鍵字只能修飾變量,不能修飾類,也不能修飾方法。併發
想要把某個變量共享,該變量的讀寫操做必須是原子性的,並用volatile關鍵字修飾。app
volatile修飾的long和double類型的變量讀寫操做是原子性的。long和double都是64位的,給long和double類型的變量賦值跟平臺相關,在有些平臺上不是原子操做。不少平臺給long和double變量賦值須要2步操做,每一步只寫32位,在這2個步驟之間,其餘線程獲取的long或double類型的變量的值的狀態是不正確的。性能
volatile變量有相似synchronized同步代碼的可見性,即每一個線程讀取到的都是最新更新的值。volatile的侷限性很大,你只能獲取volatile變量的值,直接設置volatile變量的值,不能比較以後再設置volatile的值,由於在你作比較操做的區間頗有可能有其餘線程修改了該volatile變量的值。學習
被volatile關鍵字修飾,代表該變量是要被多個線程訪問的,編譯器不會對與volatile變量相關的代碼作重排或其餘多線程下不容許的優化。沒有被volatile關鍵字修飾,編譯器會重排代碼,緩存變量的值,減小從主存中直接獲取變量的值。測試
不用volatile關鍵字修飾,有可能某個線程能夠獲取到另外一個線程設置的isActive的值,編譯器有可能緩存了isActive的值等等,使用volatile關鍵字能夠避免這些狀況。優化
雙重檢查單例模式在JDK4是有問題的,volatile能夠修復這個問題。本文的《第一個例子說明volatile變量的含義》中舉的例子就是說這個問題。spa
1. volatile關鍵字只用修飾變量,不能修飾方法和類。
2. volatile變量的值都是從主存中獲取的,而不是從線程的本地內存。
3.long和double變量被volatile關鍵字修飾以後,讀寫(賦值操做,讀取操做)都是原子操做.
4. 使用volatile關鍵字能夠避免內存不一致的錯誤;寫入volatile變量必定會比接下來的讀操做先發生。5. 從jdk5開始對volatile變量的修改對其餘的線程都是可見的;當線程讀取volatile變量的時候,會先把其餘線程中緩存着的volatile變量(若是尚未更新到主存中的時候)強制寫入到主存。
6. 除了long和double其餘的基本類型讀寫操做都是原子性的;引用類型的讀寫操做也是原子性的。
7. volatile變量只能作簡單的讀寫,沒有鎖,沒有阻塞。
8.volatile變量能夠是空的.
9. volatile不能保證原子性,好比volatile修飾的int變量++操做仍是非原子的。
10. 變量沒有在多個線程之間共享,沒有必要作任何同步的操做,好比使用volatile關鍵字修飾。
volatile關鍵字代替不了synchronized關鍵字,不過在某些場合能夠做爲替代方案。
1. volatile關鍵字只能修飾字段,而synchronized只能修飾代碼塊和方法。
2. synchronized關鍵字須要得到鎖釋放鎖,volatile關鍵字不須要。
3.synchronized代碼塊或方法在等待鎖的時候會被阻塞;volatile不是這樣的。
4. synchronized代碼塊或方法會比volatile關鍵字更影響性能。
5. volatile關鍵只同步被修飾的變量,而synchronized關鍵字卻同步代碼塊或方法中全部的變量,而且還會得到鎖釋放鎖,因此synchronized的負載更大。
6. 不能同步(synchronized)null對象,而volatile變量能夠是null的。
7. 讀取volatile變量效果等同獲取鎖,寫入volatile變量效果等同釋放鎖。