成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

java單例模式

劉福 / 2618人閱讀

摘要:?jiǎn)卫且粋€(gè)類,我們希望該類的對(duì)象在任意高并發(fā)多線程的調(diào)用下,只被初始化一次例如用于系統(tǒng)環(huán)境和詞典的加載等,后續(xù)線程均直接調(diào)用即可。我們首先來(lái)講解幾種常見的單例模式和其優(yōu)缺點(diǎn)吧。更多單例模式可以看這里。線程進(jìn)入?yún)^(qū)域,此時(shí)還未啟動(dòng)。

寫此文初衷源于昨晚線上代碼拋出的一個(gè)空指針異常。單例是一個(gè)類,我們希望該類的對(duì)象在任意高并發(fā)多線程的調(diào)用下,只被初始化一次(例如用于系統(tǒng)環(huán)境和詞典的加載等),后續(xù)線程均直接調(diào)用即可。我們首先來(lái)講解幾種常見的單例模式和其優(yōu)缺點(diǎn)吧。

1.懶漢式
// 懶漢式,線程不安全
class SingletonDemo {
    
    // 定義一個(gè)私有的靜態(tài)全局變量來(lái)保存該類的唯一實(shí)例
    private static SingletonDemo instance;
    
    private SingletonDemo() {
    
    }
    
    public static SingletonDemo getInstance() {
        // 這里可以保證只實(shí)例化一次
        if (instance == null) {  //語(yǔ)句(1)
            instance = new SingletonDemo();
        }
        return instance;
    }
}

以上代碼很明顯不能滿足我們的要求。設(shè)想有n個(gè)線程同時(shí)執(zhí)行語(yǔ)句(1),此時(shí)實(shí)例還未被初始化,因此均判斷為null,于是這n個(gè)線程每一個(gè)都新建了該類對(duì)象。

2.懶漢式改進(jìn)
// 懶漢式,線程安全,但不高效,因?yàn)槿魏螘r(shí)候只能有一個(gè)線程調(diào)用getInstance()方法。
class SingletonDemo2 {
    
    private static SingletonDemo2 instance;
    
    private SingletonDemo2() {}
    
    public static synchronized SingletonDemo2 getInstance() { //語(yǔ)句(1)
        if (instance == null) { //區(qū)域(1)
            instance = new SingletonDemo2();
        }
        return instance;
    }
}

我們使用synchronized來(lái)強(qiáng)制使每個(gè)線程串行執(zhí)行語(yǔ)句(1),因此永遠(yuǎn)只有第一個(gè)線程新建了該類對(duì)象。那么這段代碼缺點(diǎn)在哪呢?速度慢。假設(shè)現(xiàn)在有1000個(gè)線程,均在語(yǔ)句(1)處排隊(duì),當(dāng)?shù)谝粋€(gè)線程創(chuàng)建新對(duì)象后,剩下999個(gè)線程仍然需要排隊(duì),進(jìn)入?yún)^(qū)域(1)判斷不為空并返回。

這里懶漢式的意思是:要用的時(shí)候才去new。區(qū)別于接下來(lái)要講的:

3.餓漢式
/**
 * 餓漢式,單例的實(shí)例被聲明成static和final,在第一次加載到內(nèi)存中時(shí)會(huì)初始化。
 * 
 * 缺點(diǎn):
 * 不是一種懶加載(lazy initlalization),在一些場(chǎng)景中無(wú)法使用:
 * 譬如Singleton實(shí)例的創(chuàng)建時(shí)以來(lái)參數(shù)或者配置文件的,在getInstance()之前必須調(diào)用
 */
class SingletonDemo5 {
    // 類加載時(shí)就初始化
    private static final SingletonDemo5 instance = new SingletonDemo5();
    
    private SingletonDemo5() {}
    
    public static SingletonDemo5 getInstance() {
        return instance;
    }
}

這段代碼利用了jvm對(duì)private static final只初始化一次的特性,可以解決多線程問(wèn)題,但是當(dāng)我們要在getInstance()前做一些配置工作(例如初始化數(shù)據(jù)庫(kù)連接等),那么這種方式就捉襟見肘了。

4.雙重檢驗(yàn)鎖
// 雙重檢驗(yàn)鎖(double checked) 
class SingletonDemo3 {
    
    private static volatile SingletonDemo3 instance;
    
    private SingletonDemo3() {}
    
    public static SingletonDemo3 getInstance() {
        if (instance == null) {    // 區(qū)域(1)
            synchronized (SingletonDemo3.class) {  // 區(qū)域(2)
                if (instance == null) {
                    instance = new SingletonDemo3(); // 語(yǔ)句(1)
                }
            }
        }
        return instance;
    }
}

雙重檢驗(yàn)鎖(double checked)。注意到它有2次判斷,一次在同步塊內(nèi),一次在同步塊外。假設(shè)現(xiàn)在有4個(gè)線程T1,T2,T3,T4。T1,T2進(jìn)入了區(qū)域(1),T3,T4還沒(méi)啟動(dòng)。T1能進(jìn)入?yún)^(qū)域(2)創(chuàng)建instance成功,之后T2進(jìn)入?yún)^(qū)域(2),判斷非空并出來(lái)。此時(shí)T3,T4啟動(dòng)了,不會(huì)進(jìn)入?yún)^(qū)域(1),且無(wú)需等待鎖。

instance變量聲明成volatile, 它可以禁止指令重排序優(yōu)化。
volatile的兩個(gè)作用:

禁止指令重排優(yōu)化。

所修飾的變量一旦被修改,其他線程立即可見。(但是非原子操作,即其他線程可以感知到變量被修改,但無(wú)法使用val += 1這種語(yǔ)句使其原子增加。)因此可用于1讀者n寫者的場(chǎng)景,例如點(diǎn)擊"游戲退出按鈕",其他(金幣累加/音效)線程將立即感知到。

更多單例模式可以看這里。

5.異常問(wèn)題回放

昨晚報(bào)出異常的代碼片段如下:

class WrongSample {
    
    private volatile static WrongSample instance;
    
    private WrongSample() {

    }
    
    public static WrongSample getInstance() {
        if (instance == null) {    // 區(qū)域(1)
            synchronized (WrongSample.class) {  // 區(qū)域(2)
                if (instance == null) {
                    instance = new WrongSample(); // 語(yǔ)句(1)
                    instance.init(); //語(yǔ)句(2)
                }
            }
        }
        return instance;
    }

    private void init() {
    
    }
}

該代碼使用雙重檢驗(yàn)鎖構(gòu)建了一個(gè)單例,且對(duì)單例進(jìn)行初始化。那么空指針異常拋出對(duì)原因在哪呢?設(shè)想現(xiàn)在有線程T1,T2。線程T1進(jìn)入?yún)^(qū)域(2),T2此時(shí)還未啟動(dòng)。T1執(zhí)行了語(yǔ)句(1),但并未執(zhí)行語(yǔ)句2,此時(shí)instance已經(jīng)不是null,所以T2啟動(dòng)時(shí)在區(qū)域(1)判斷非null將直接返回instance,但T2并未被初始化,由是產(chǎn)生異常。

解決方案:初始化操作放進(jìn)構(gòu)造函數(shù),執(zhí)行語(yǔ)句(1)時(shí)里暗含里執(zhí)行構(gòu)造函數(shù)。代碼如下:

class WrongSample {
    
    private volatile static WrongSample instance;
    
    private WrongSample() {
        init();
    }
    
    public static WrongSample getInstance() {
        if (instance == null) {    // 區(qū)域(1)
            synchronized (WrongSample.class) {  // 區(qū)域(2)
                if (instance == null) {
                    instance = new WrongSample(); // 語(yǔ)句(1)
                }
            }
        }
        return instance;
    }

    private void init() {
    
    }
}

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/70418.html

相關(guān)文章

  • 單例模式你會(huì)幾種寫法?

    摘要:使用靜態(tài)類體現(xiàn)的是基于對(duì)象,而使用單例設(shè)計(jì)模式體現(xiàn)的是面向?qū)ο蟆6帉憜卫J降拇a編寫單例模式的代碼其實(shí)很簡(jiǎn)單,就分了三步將構(gòu)造函數(shù)私有化在類的內(nèi)部創(chuàng)建實(shí)例提供獲取唯一實(shí)例的方法餓漢式根據(jù)上面的步驟,我們就可以輕松完成創(chuàng)建單例對(duì)象了。 前言 只有光頭才能變強(qiáng) 回顧前面: 給女朋友講解什么是代理模式 包裝模式就是這么簡(jiǎn)單啦 本來(lái)打算沒(méi)那么快更新的,這陣子在刷Spring的書籍。在看...

    solocoder 評(píng)論0 收藏0
  • Java 雙重加鎖單例java 內(nèi)存重排序特性

    摘要:關(guān)于對(duì)于重排序的講解,強(qiáng)烈推薦閱讀程曉明寫的深入理解內(nèi)存模型二重排序。語(yǔ)義語(yǔ)義單線程下,為了優(yōu)化可以對(duì)操作進(jìn)行重排序。編譯器和處理器為單個(gè)線程實(shí)現(xiàn)了語(yǔ)義,但對(duì)于多線程并不實(shí)現(xiàn)語(yǔ)義。雙重加載的單例模式分析即雙重檢查加鎖。 版權(quán)聲明:本文由吳仙杰創(chuàng)作整理,轉(zhuǎn)載請(qǐng)注明出處:https://segmentfault.com/a/1190000009231182 1. 引言 在開始分析雙重加鎖單...

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

    摘要:?jiǎn)卫J疥P(guān)注的重點(diǎn)私有構(gòu)造器線程安全延遲加載序列化和反序列化安全反射攻擊安全相關(guān)設(shè)計(jì)模式單例模式和工廠模式工廠類可以設(shè)計(jì)成單例模式。 0x01.定義與類型 定義:保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)全局訪問(wèn)點(diǎn) 類型:創(chuàng)建型 UML showImg(https://segmentfault.com/img/bVbtDJ2?w=402&h=268); 單例模式的基本要素 私有的構(gòu)造方...

    陸斌 評(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è)比較簡(jiǎn)單的模式,其定義如下: 保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。 或者 Ensure a class has only one instance, and provide a global point of ac...

    k00baa 評(píng)論0 收藏0
  • Java基礎(chǔ)學(xué)習(xí)——多線程之單例設(shè)計(jì)模式(轉(zhuǎn))

    摘要:總之,選擇單例模式就是為了避免不一致狀態(tài),避免政出多頭。二餓漢式單例餓漢式單例類在類初始化時(shí),已經(jīng)自行實(shí)例化靜態(tài)工廠方法餓漢式在類創(chuàng)建的同時(shí)就已經(jīng)創(chuàng)建好一個(gè)靜態(tài)的對(duì)象供系統(tǒng)使用,以后不再改變,所以天生是線程安全的。 概念:  Java中單例模式是一種常見的設(shè)計(jì)模式,單例模式的寫法有好幾種,這里主要介紹兩種:懶漢式單例、餓漢式單例。  單例模式有以下特點(diǎn):  1、單例類只能有一個(gè)實(shí)例?!?..

    dendoink 評(píng)論0 收藏0
  • 從未這么明白的設(shè)計(jì)模式(一):單例模式

    摘要:一般來(lái)說(shuō),這種單例實(shí)現(xiàn)有兩種思路,私有構(gòu)造器,枚舉。而這種方式又分了飽漢式,餓漢式。通過(guò)關(guān)鍵字防止指令重排序。什么是單例?為什么要用單例? 一個(gè)類被設(shè)計(jì)出來(lái),就代表它表示具有某種行為(方法),屬性(成員變量),而一般情況下,當(dāng)我們想使用這個(gè)類時(shí),會(huì)使用new關(guān)鍵字,這時(shí)候jvm會(huì)幫我們構(gòu)造一個(gè)該類的實(shí)例。而我們知道,對(duì)于new這個(gè)關(guān)鍵字以及該實(shí)例,相對(duì)而言是比較耗費(fèi)資源的。所以如果我們能夠想...

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

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

0條評(píng)論

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