国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

單例模式

Backache / 979人閱讀

摘要:構(gòu)造函數(shù)被調(diào)用或者,我們利用初始化塊,在初始化的時(shí)候就完成實(shí)例化構(gòu)造器被調(diào)用雙重檢查鎖定避免懶漢模式造成性能低下的另一個(gè)思路就是雙重檢查鎖定。

1. 什么是單例

保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。適用于:

當(dāng)類只能有一個(gè)實(shí)例而且客戶可以從一個(gè)眾所周知的訪問點(diǎn)訪問它時(shí)。

當(dāng)這個(gè)唯一實(shí)例應(yīng)該是通過子類化可擴(kuò)展的,并且客戶應(yīng)該無需更改代碼就能使用一個(gè)擴(kuò)展的實(shí)例時(shí)。

在單例模式中,有下列參與者:

Singleton:

定義一個(gè)Instance操作,允許客戶訪問它的唯一實(shí)例。Instance是一個(gè)類操作。

可能負(fù)責(zé)創(chuàng)建它自己的唯一實(shí)例。

2. 不考慮多線程的情況下的單例

下面就是一個(gè)單例的實(shí)現(xiàn):Singleton0當(dāng)然,這個(gè)示例在多線程下有問題

// 單例程序: Singleton0 

class Printer{
    private static Printer printer;
    
    private Printer(){
        
    }
    
    public static Printer getInstance(){
        if(printer == null){
            printer = new Printer();
        }
        return printer;
    }
    
}

public class Singleton {

    public static void main(String[] args) {
        Printer p1 = Printer.getInstance();
        Printer p2 = Printer.getInstance();
        System.out.println(p1);
        System.out.println(p2);
    }

}

/* 運(yùn)行結(jié)果:
 * Printer@659e0bfd
 * Printer@659e0bfd
 * 完全一樣,表示只創(chuàng)建了Printer對(duì)象的一個(gè)實(shí)例
 */
3. 多線程環(huán)境下的單例

很可惜,上面的單例程序在多線程環(huán)境下,會(huì)華麗麗的出錯(cuò)!

3.1. 上述單例在多線程環(huán)境下的問題

我們修改一下Singleton0,成為如下形式:Singleton1

public class Singleton1 {
    
    private static Singleton1 s1;
    
    private Singleton1(){
        System.out.println("構(gòu)造函數(shù)被調(diào)用!");
    }
    
    public static Singleton1 getInstance(){
        if(s1 == null){
            s1 = new Singleton1();
        }
        return s1;
    }
}

// 測試程序,采用JUnit 4.x來測試
import org.junit.Test;

public class Singleton1Test implements Runnable{
    
    @Override
    public void run() {
        Singleton1.getInstance();
    }
    
    @Test
    public void test() {
        for (int i = 0; i < 100000; i++) {
            Thread t = new Thread(new Singleton1Test(), "AnyThreadName");
            t.start();
        }
    }
}

/* 運(yùn)行結(jié)果:(可以發(fā)現(xiàn),構(gòu)造函數(shù)被多次調(diào)用!說明無法保證單例)
 * 構(gòu)造函數(shù)被調(diào)用!
 * 構(gòu)造函數(shù)被調(diào)用!
 * 構(gòu)造函數(shù)被調(diào)用!
 * 構(gòu)造函數(shù)被調(diào)用!
 * 構(gòu)造函數(shù)被調(diào)用!
 */

原因很簡單,在多線程的情況下,調(diào)用 Singleton1.getInstance() 的時(shí)候,可能會(huì)多個(gè)線程同時(shí)調(diào)用到,這個(gè)時(shí)候構(gòu)造函數(shù) Singleton1() 還沒有把 s1 實(shí)例化出來。這個(gè)時(shí)候判斷 s1 == null 是對(duì)的,所以多個(gè)線程都會(huì)去執(zhí)行:

if(s1 == null){
    s1 = new Singleton1();
}

所以,這個(gè)構(gòu)造函數(shù)就會(huì)被執(zhí)行多次!

知道了這個(gè)原因,我們就可以很方便的找到解決方法,那就是:懶漢模式。

3.2. 懶漢模式

既然是問題出在 getInstance() 上,那么我們就把這個(gè)方法設(shè)置成synchronized,這樣就可以保證同步了,于是我們修改成 Singleton2

/**
 *  這種寫法能夠在多線程中很好的工作,而且看起來它也具備很好的lazy loading。
 *  但是,遺憾的是,效率很低,99%情況下不需要同步。
 * 
 * @author martin.wang
 *
 */
public class Singleton2 {
    private static Singleton2 s2;
    
    private Singleton2(){
        System.out.println("構(gòu)造函數(shù)被調(diào)用!");
    }
    
    public static synchronized Singleton2 getInstance(){
        if(s2 == null){
            s2 = new Singleton2();
        }
        return s2;
    }
}

這個(gè)方式的最大問題就是:效率太低了。我們知道 synchronized 關(guān)鍵字很消耗資源,而且99%以上的可能性是不用 synchronized 的。每次都要 synchronized 有必要嗎?那么我們就有了2種解決方案:

餓漢模式。干脆一開始就給你初始化算了。

雙重檢查鎖定。只在必要的時(shí)候用 synchronized

3.3. 餓漢模式

餓漢模式避免了在 getInstance 的時(shí)候的判斷,所以效率高一點(diǎn)。不過也不是無懈可擊,如果這個(gè)構(gòu)造的過程很消費(fèi)時(shí)間,那么每次classloader的時(shí)間會(huì)非常長,沒有起到 lazyload 的效果。

public class Singleton3{
    private static Singleton3 singleton3 = new Singleton3();

    private Singleton3() {
        System.out.println("構(gòu)造函數(shù)被調(diào)用!");
    }

    public static Singleton3 getInstance() {
        return singleton3;
    }
}

或者,我們利用初始化塊,在初始化的時(shí)候就完成實(shí)例化

public class Singleton4 {
    private static Singleton4 s4 = null;
    
    static {
        s4 = new Singleton4();
    }
    
    private Singleton4(){
        System.out.println("構(gòu)造器被調(diào)用");
    }
    
    public static Singleton4 getInstance(){
        return s4;
    }
}
3.4. 雙重檢查鎖定

避免懶漢模式造成性能低下的另一個(gè)思路就是:雙重檢查鎖定。原理就是:

Singleton1 示例中造成問題的原因是 getInstance() 不同步

Singleton2 示例中造成性能低下的原因是不管三七二十一全同步

那么,我們就只檢查可能存在 同步問題 的代碼,讓代碼只在 可能存在問題的時(shí)候 再去做同步。

3.4.1. 雙重檢查鎖定的“實(shí)現(xiàn)”(有問題的!)
public class Singleton7 {
    private static Singleton7 s7;
    
    private Singleton7() {
        System.out.println("構(gòu)造函數(shù)被調(diào)用");
    }
    
    public static Singleton7 getInstance() {
        if(s7 == null) {
            synchronized (Singleton7.class) {   // A
                if(s7 == null) {                // B
                    s7 = new Singleton7();      // C
                }
            }
        }
        return s7;
    }
}

思路分析:

如果 s7 == null ,那么這個(gè)時(shí)候要同步了。

在注釋 A 的里面,設(shè)置一個(gè)同步鎖

如果線程 T1 訪問同步塊 A 中的代碼的時(shí)候,線程 T2A 附近等待釋放鎖。

T1 線程完成,這個(gè)時(shí)候 T2 線程開始運(yùn)行 A 中的代碼。這個(gè)時(shí)候 s7 已經(jīng)被 T1 線程初始化了,執(zhí)行 B 的時(shí)候會(huì)返回 false,不會(huì)去執(zhí)行構(gòu)造函數(shù)。

不過,這個(gè)辦法還是不能保證完美無缺,還存在至少是理論上的缺陷。

3.4.2. 原因分析

雙重檢查鎖定背后的理論是完美的。不幸地是,現(xiàn)實(shí)完全不同。雙重檢查鎖定的問題是:并不能保證它會(huì)在單處理器或多處理器計(jì)算機(jī)上順利運(yùn)行。

雙重檢查鎖定失敗的問題并不歸咎于 JVM 中的實(shí)現(xiàn) bug,而是歸咎于 Java 平臺(tái)內(nèi)存模型。內(nèi)存模型允許所謂的“無序?qū)懭搿薄?/p>

也就是說,不能保證 A, B, C 是按順序運(yùn)行的,這個(gè)可以Google一下 指令重排,這里不展開了。

說實(shí)話,我自己測試了100+次,并沒有出現(xiàn)構(gòu)造函數(shù)出現(xiàn)2次或以上的情況。出現(xiàn)這情況的概率很小很小。Java的內(nèi)存模型很復(fù)雜,牽涉到具體的JVM實(shí)現(xiàn)。
3.4.3. 修改后的加強(qiáng)版
public class Singleton7 {
    private static volatile Singleton7 s7;
    
    private Singleton7() {
        System.out.println("構(gòu)造函數(shù)被調(diào)用");
    }
    
    public static Singleton7 getInstance() {
        if(s7 == null) {
            synchronized (Singleton7.class) {   // A
                if(s7 == null) {                // B
                    s7 = new Singleton7();      // C
                }
            }
        }
        return s7;
    }
}
不過,據(jù)說這個(gè)也不是特別靠譜,我不去深究了。

看到這里,你是否有種想罵人的沖動(dòng)?什么鬼,做個(gè)單例模式就這么難啊。有沒有更方便的辦法?有,還不止一種:

3.5. 靜態(tài)內(nèi)部類法
public class Singleton5 {

    private static class SingletonHolder{
        private static final Singleton5 INSTANCE = new Singleton5();
    }
    
    private Singleton5() {
        System.out.println("構(gòu)造函數(shù)被調(diào)用");
    }
    
    public static final Singleton5 getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

// 測試方法:
import org.junit.Test;

public class Singleton5Test implements Runnable{

    @Override
    public void run() {
        Singleton5.getInstance();
    }
    
    @Test
    public void test() {
        for (int i = 0; i < 10000; i++) {
            Thread t = new Thread(new Singleton4Test(), "T5");
            t.start();
        }
    }
    
}
3.6. 枚舉類法

枚舉類發(fā)是《Effective Java》的作者 Josh Bloch 推薦的一種實(shí)現(xiàn)方式,除了具有上述方法的優(yōu)點(diǎn)的話,還能防止反序列化重新創(chuàng)建新的對(duì)象、防止被反射攻擊。超級(jí)牛叉!

TODO:現(xiàn)在還沒有去寫利用枚舉類法防止反序列化,反射攻擊的測試用例。希望以后來填坑。
public enum Singleton6 {
    INSTANCE;
    
    private Singleton6() {
        System.out.println("構(gòu)造函數(shù)被調(diào)用");
    }
    
    protected void doSomething() {
        
    }
}

// 測試:
import org.junit.Test;

public class Singleton6Test implements Runnable{
    
    
    @Override
    public void run() {
        Singleton6.INSTANCE.doSomething();
    }

    @Test
    public void test() {
        for (int i = 0; i < 1000; i++) {
            Thread t = new Thread(new Singleton6Test());
            t.start();
        }
    }
}
4. 總結(jié)

如果沒有什么特別需要,我個(gè)人認(rèn)為還是用餓漢方式算了,簡單有效。如果有懶加載要求,就用靜態(tài)內(nèi)部類法,也不錯(cuò)。有反序列化要求的,就用枚舉類法。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/68765.html

相關(guān)文章

  • Android中的設(shè)計(jì)模式單例模式

    摘要:總結(jié)單例是運(yùn)用頻率很高的模式,因?yàn)榭蛻舳藳]有高并發(fā)的情況,選擇哪種方式并不會(huì)有太大的影響,出于效率考慮,推薦使用和靜態(tài)內(nèi)部類實(shí)現(xiàn)單例模式。 單例模式介紹 單例模式是應(yīng)用最廣的模式之一,也可能是很多人唯一會(huì)使用的設(shè)計(jì)模式。在應(yīng)用單例模式時(shí),單例對(duì)象的類必須保證只用一個(gè)實(shí)例存在。許多時(shí)候整個(gè)系統(tǒng)只需要一個(gè)全局對(duì)象,這樣有利于我么能協(xié)調(diào)整個(gè)系統(tǒng)整體的行為。 單例模式的使用場景 確保某個(gè)類有且...

    yzd 評(píng)論0 收藏0
  • JavaScript設(shè)計(jì)模式----單例模式

    摘要:不符合設(shè)計(jì)模式中的單一職責(zé)的概念。引入代理實(shí)現(xiàn)單例模式引入代理實(shí)現(xiàn)單例模式的特點(diǎn)我們負(fù)責(zé)管理單例的邏輯移到了代理類中。的單例模式對(duì)比在以上的代碼中實(shí)現(xiàn)的單例模式都混入了傳統(tǒng)面向?qū)ο笳Z言的特點(diǎn)。 聲明:這個(gè)系列為閱讀《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》 ----曾探@著一書的讀書筆記 1.單例模式的特點(diǎn)和定義 保證一個(gè)類僅有一個(gè)實(shí)例,并且提供一個(gè)訪問它的全局訪問點(diǎn)。 2.傳統(tǒng)面向?qū)?..

    selfimpr 評(píng)論0 收藏0
  • Java設(shè)計(jì)模式-單例模式(Singleton Pattern)

    摘要:如果需要防范這種攻擊,請(qǐng)修改構(gòu)造函數(shù),使其在被要求創(chuàng)建第二個(gè)實(shí)例時(shí)拋出異常。單例模式與單一職責(zé)原則有沖突。源碼地址參考文獻(xiàn)設(shè)計(jì)模式之禪 定義 單例模式是一個(gè)比較簡單的模式,其定義如下: 保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。 或者 Ensure a class has only one instance, and provide a global point of ac...

    k00baa 評(píng)論0 收藏0
  • Java 設(shè)計(jì)模式單例模式

    摘要:在設(shè)計(jì)模式一書中,將單例模式稱作單件模式。通過關(guān)鍵字,來保證不會(huì)同時(shí)有兩個(gè)線程進(jìn)入該方法的實(shí)例對(duì)象改善多線程問題為了符合大多數(shù)程序,很明顯地,我們需要確保單例模式能在多線程的情況下正常工作。 在《Head First 設(shè)計(jì)模式》一書中,將單例模式稱作單件模式。這里為了適應(yīng)大環(huán)境,把它稱之為大家更熟悉的單例模式。 一、了解單例模式 1.1 什么是單例模式 單例模式確保一個(gè)類只有一個(gè)實(shí)例,...

    everfight 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

Backache

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<