Java單例模式

單例模式之餓漢模式:

/**
 * 
 * @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的內存模型

Java的內存模型(JMM)能夠保證一個線程修改了某個變量,該變量的改變對另外一個線程是可見的,也就是說另外一個線程取得的這個變量的值,必定是前一個線程修改以後的值。這個也被稱做"happens-before" 。多線程

volatile關鍵字使用場所

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關鍵字修飾,編譯器會重排代碼,緩存變量的值,減小從主存中直接獲取變量的值。測試

用例子來詳細完全說明Java的volatile關鍵字工做原理

不用volatile關鍵字修飾,有可能某個線程能夠獲取到另外一個線程設置的isActive的值,編譯器有可能緩存了isActive的值等等,使用volatile關鍵字能夠避免這些狀況。優化

  • 雙重檢查單例模式在JDK4是有問題的,volatile能夠修復這個問題。本文的《第一個例子說明volatile變量的含義》中舉的例子就是說這個問題。spa

volatile關鍵字的總結

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關鍵字修飾。

synchronized和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變量效果等同釋放鎖。

相關文章
相關標籤/搜索