亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專(zhuān)欄INFORMATION COLUMN

一起學(xué)設(shè)計(jì)模式 - 單例模式

Keven / 2115人閱讀

摘要:懶漢非線程安全,需要用一定的風(fēng)騷操作控制,裝逼失敗有可能導(dǎo)致看一周的海綿寶寶餓漢天生線程安全,的時(shí)候就已經(jīng)實(shí)例化好,該操作過(guò)于風(fēng)騷會(huì)造成資源浪費(fèi)單例注冊(cè)表初始化的時(shí)候,默認(rèn)單例用的就是該方式特點(diǎn)私有構(gòu)造方法,只能有一個(gè)實(shí)例。

單例設(shè)計(jì)模式(Singleton Pattern)是最簡(jiǎn)單且常見(jiàn)的設(shè)計(jì)模式之一,主要作用是提供一個(gè)全局訪問(wèn)且只實(shí)例化一次的對(duì)象,避免多實(shí)例對(duì)象的情況下引起邏輯性錯(cuò)誤(實(shí)例化數(shù)量可控)...

概述

Java中,單例模式主要分三種:懶漢式單例、餓漢式單例、登記式單例三種。

懶漢:非線程安全,需要用一定的風(fēng)騷操作控制,裝逼失敗有可能導(dǎo)致看一周的海綿寶寶

餓漢:天生線程安全,ClassLoad的時(shí)候就已經(jīng)實(shí)例化好,該操作過(guò)于風(fēng)騷會(huì)造成資源浪費(fèi)

單例注冊(cè)表:Spring初始化Bean的時(shí)候,默認(rèn)單例用的就是該方式

特點(diǎn)

私有構(gòu)造方法,只能有一個(gè)實(shí)例。

私有靜態(tài)引用指向自己實(shí)例,必須是自己在內(nèi)部創(chuàng)建的唯一實(shí)例。

單例類(lèi)給其它對(duì)象提供的都是自己創(chuàng)建的唯一實(shí)例

案例

在計(jì)算機(jī)系統(tǒng)中,內(nèi)存、線程、CPU等使用情況都可以再任務(wù)管理器中看到,但始終只能打開(kāi)一個(gè)任務(wù)管理器,它在Windows操作系統(tǒng)中是具備唯一性的,因?yàn)閺椂鄠€(gè)框多次采集數(shù)據(jù)浪費(fèi)性能不說(shuō),采集數(shù)據(jù)存在誤差那就有點(diǎn)逗比了不是么...

每臺(tái)電腦只有一個(gè)打印機(jī)后臺(tái)處理程序

線程池的設(shè)計(jì)一般也是采用單例模式,方便對(duì)池中的線程進(jìn)行控制

注意事項(xiàng)

實(shí)現(xiàn)方式種類(lèi)較多,有的非線程安全方式的創(chuàng)建需要特別注意,且在使用的時(shí)候盡量根據(jù)場(chǎng)景選取較優(yōu)的,線程安全了還需要去考慮性能問(wèn)題。

不適用于變化的對(duì)象,如果同一類(lèi)型的對(duì)象總是要在不同的用例場(chǎng)景發(fā)生變化,單例就會(huì)引起數(shù)據(jù)的錯(cuò)誤,不能保存彼此的狀態(tài)。

沒(méi)有抽象層,擴(kuò)展有困難。

職責(zé)過(guò)重,在一定程度上違背了單一職責(zé)原則。

使用時(shí)不能用反射模式創(chuàng)建單例,否則會(huì)實(shí)例化一個(gè)新的對(duì)象

解鎖姿勢(shì)

第一種:?jiǎn)我粰z查(懶漢)非線程安全

public class LazyLoadBalancer {

    private static LazyLoadBalancer loadBalancer;
    private List servers = null;

    private LazyLoadBalancer() {
        servers = new ArrayList<>();
    }

    public void addServer(String server) {
        servers.add(server);
    }

    public String getServer() {
        Random random = new Random();
        int i = random.nextInt(servers.size());
        return servers.get(i);
    }

    public static LazyLoadBalancer getInstance() {
        // 第一步:假設(shè)T1,T2兩個(gè)線程同時(shí)進(jìn)來(lái)且滿(mǎn)足 loadBalancer == null
        if (loadBalancer == null) {
            // 第二步:那么 loadBalancer 即會(huì)被實(shí)例化2次
            loadBalancer = new LazyLoadBalancer();
        }
        return loadBalancer;
    }

    public static void main(String[] args) {
        LazyLoadBalancer balancer1 = LazyLoadBalancer.getInstance();
        LazyLoadBalancer balancer2 = LazyLoadBalancer.getInstance();
        System.out.println("hashCode:"+balancer1.hashCode());
        System.out.println("hashCode:"+balancer2.hashCode());
        balancer1.addServer("Server 1");
        balancer2.addServer("Server 2");
        IntStream.range(0, 5).forEach(i -> System.out.println("轉(zhuǎn)發(fā)至:" + balancer1.getServer()));
    }
}

日志

hashCode:460141958
hashCode:460141958
轉(zhuǎn)發(fā)至:Server 2
轉(zhuǎn)發(fā)至:Server 2
轉(zhuǎn)發(fā)至:Server 2
轉(zhuǎn)發(fā)至:Server 1
轉(zhuǎn)發(fā)至:Server 2

分析: 在單線程環(huán)境一切正常,balancer1balancer2兩個(gè)對(duì)象的hashCode一模一樣,由此可以判斷出堆棧中只有一份內(nèi)容,不過(guò)該代碼塊中存在線程安全隱患,因?yàn)槿狈Ω?jìng)爭(zhēng)條件,多線程環(huán)境資源競(jìng)爭(zhēng)的時(shí)候就顯得不太樂(lè)觀了,請(qǐng)看上文代碼注釋內(nèi)容

第二種:無(wú)腦上鎖(懶漢)線程安全,性能較差,第一種升級(jí)版

public synchronized static LazyLoadBalancer getInstance() {
    if (loadBalancer == null) {
        loadBalancer = new LazyLoadBalancer();
    }
    return loadBalancer;
}

分析: 毫無(wú)疑問(wèn),知道synchronized關(guān)鍵字的都知道,同步方法在鎖沒(méi)釋放之前,其它線程都在排隊(duì)候著呢,想不安全都不行啊,但在安全的同時(shí),性能方面就顯得短板了,我就初始化一次,你丫的每次來(lái)都上個(gè)鎖,不累的嗎(沒(méi)關(guān)系,它是為了第三種做鋪墊的)..

第三種:雙重檢查鎖(DCL),完全就是前兩種的結(jié)合體啊,有木有,只是將同步方法升級(jí)成了同步代碼塊

//劃重點(diǎn)了 **volatile**
private volatile static LazyLoadBalancer loadBalancer;

public static LazyLoadBalancer getInstance() {
    if (loadBalancer == null) {
        synchronized (LazyLoadBalancer.class) {
            if (loadBalancer == null) {
                loadBalancer = new LazyLoadBalancer();
            }
        }
    }
    return loadBalancer;
}

1.假設(shè)new LazyLoadBalancer()加載內(nèi)容過(guò)多
2.因重排而導(dǎo)致loadBalancer提前不為空
3.正好被其它線程觀察到對(duì)象非空直接返回使用

mem = allocate();                  //LazyLoadBalancer 分配內(nèi)存
instance = mem;                     //注意當(dāng)前實(shí)例已經(jīng)不為空了                      
initByLoadBalancer(instance);      //但是還有其它實(shí)例未初始化

存在問(wèn)題: 首先我們一定要清楚,DCL是不能保證線程安全的,稍微了解過(guò)JVM的就清楚,對(duì)比C/C++它始終缺少一個(gè)正式的內(nèi)存模型,所以為了提升性能,它還會(huì)做一次指令重排操作,這個(gè)時(shí)候就會(huì)導(dǎo)致loadBalancer提前不為空,正好被其它線程觀察到對(duì)象非空直接返回使用(但實(shí)際還有部分內(nèi)容沒(méi)加載完成)

解決方案:volatile修飾loadBalancer,因?yàn)?b>volatile修飾的成員變量可以確保多個(gè)線程都能夠順序處理,它會(huì)屏蔽JVM指令重排帶來(lái)的性能優(yōu)化

volatile詳解:http://blog.battcn.com/2017/10/18/java/thread/thread-volatile/

第四種:Demand Holder (懶漢)線程安全,推薦使用

private LazyLoadBalancer() {}

private static class LoadBalancerHolder {
    //在JVM中 final 對(duì)象只會(huì)被實(shí)例化一次,無(wú)法修改
    private final static LazyLoadBalancer INSTANCE = new LazyLoadBalancer();
}

public static LazyLoadBalancer getInstance() {
    return LoadBalancerHolder.INSTANCE;
}

分析:Demand Holder中,我們?cè)?b>LazyLoadBalancer里增加一個(gè)靜態(tài)(static)內(nèi)部類(lèi),在該內(nèi)部類(lèi)中創(chuàng)建單例對(duì)象,再將
該單例對(duì)象通過(guò)getInstance()方法返回給外部使用,由于靜態(tài)單例對(duì)象沒(méi)有作為LazyLoadBalancer的成員變量直接實(shí)例化,類(lèi)加載時(shí)并不會(huì)實(shí)例化LoadBalancerHolder,因此既可以實(shí)現(xiàn)延遲加載,又可以保證線程安全,不影響系統(tǒng)性能(居家旅行必備良藥?。?/p>

第五種:枚舉特性(懶漢)線程安全

enum Lazy {
    INSTANCE;
    private LazyLoadBalancer loadBalancer;

    //枚舉的特性,在JVM中只會(huì)被實(shí)例化一次
    Lazy() {
        loadBalancer = new LazyLoadBalancer();
    }

    public LazyLoadBalancer getInstance() {
        return loadBalancer;
    }
}

分析: 相比上一種,該方式同樣是用到了JAVA特性:枚舉類(lèi)保證只有一個(gè)實(shí)例(即使使用反射機(jī)制也無(wú)法多次實(shí)例化一個(gè)枚舉量)

第六種:餓漢單例(天生線程安全),

public class EagerLoadBalancer {
    private final static EagerLoadBalancer INSTANCE = new EagerLoadBalancer();

    private EagerLoadBalancer() {}

    public static EagerLoadBalancer getInstance() {
        return INSTANCE;
    }
}

分析: 利用ClassLoad機(jī)制,在加載時(shí)進(jìn)行實(shí)例化,同時(shí)靜態(tài)方法只在編譯期間執(zhí)行一次初始化,也就只有一個(gè)對(duì)象。使用的時(shí)候已被初始化完畢可以直接調(diào)用,但是相比懶漢模式,它在使用的時(shí)候速度最快,但這玩意就像自己挖的坑哭著也得跳,你不用也得初始化一份在內(nèi)存中占個(gè)坑...

- 說(shuō)點(diǎn)什么

全文代碼:https://gitee.com/battcn/design-pattern/tree/master/Chapter2/battcn-singleton

個(gè)人QQ:1837307557

battcn開(kāi)源群(適合新手):391619659

微信公眾號(hào):battcn(歡迎調(diào)戲)

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

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

相關(guān)文章

  • java并發(fā)編程學(xué)習(xí)之Volatile

    摘要:但是的語(yǔ)義不足以確保遞增操作的原子性,在多線程的情況下,線程不一定是安全的。檢查某個(gè)狀態(tài)標(biāo)記,以判斷是否退出循環(huán)某個(gè)方法這邊和用普通的變量的區(qū)別是,在多線程的情況下,取到后,的值被改變了,判斷會(huì)不正確。 多線程為什么是不安全的 這邊簡(jiǎn)單的講述一下,參考java并發(fā)編程學(xué)習(xí)之synchronize(一) 當(dāng)線程A和線程B同時(shí)進(jìn)入num = num + value; 線程A會(huì)把num的值...

    thekingisalwaysluc 評(píng)論0 收藏0
  • 一起學(xué)設(shè)計(jì)模式 - 享元模式

    摘要:享元模式屬于結(jié)構(gòu)型模式的一種,又稱(chēng)輕量級(jí)模式,通過(guò)共享技術(shù)有效地實(shí)現(xiàn)了大量細(xì)粒度對(duì)象的復(fù)用概述兩種結(jié)構(gòu)狀態(tài)內(nèi)部狀態(tài)享元對(duì)象內(nèi)部不隨外界環(huán)境改變而改變的共享部分。 享元模式(Flyweight Pattern)屬于結(jié)構(gòu)型模式的一種,又稱(chēng)輕量級(jí)模式,通過(guò)共享技術(shù)有效地實(shí)現(xiàn)了大量細(xì)粒度對(duì)象的復(fù)用... 概述 兩種結(jié)構(gòu)狀態(tài) 內(nèi)部狀態(tài):享元對(duì)象內(nèi)部不隨外界環(huán)境改變而改變的共享部分。 外部狀態(tài)...

    Jason 評(píng)論0 收藏0
  • python之單例模式和工廠模式

    摘要:在工廠方法模式中,我們會(huì)遇到一個(gè)問(wèn)題,當(dāng)產(chǎn)品非常多時(shí),繼續(xù)使用工廠方法模式會(huì)產(chǎn)生非常多的工廠類(lèi)。從簡(jiǎn)單工廠模式到抽象工廠模式,我們都是在用后一種模式解決前一種模式的缺陷,都是在最大程度降低代碼的耦合性。 單例模式 所謂單例模式,也就是說(shuō)不管什么時(shí)候我們要確保只有一個(gè)對(duì)象實(shí)例存在。很多情況下,整個(gè)系統(tǒng)中只需要存在一個(gè)對(duì)象,所有的信息都從這個(gè)對(duì)象獲取,比如系統(tǒng)的配置對(duì)象,或者是線程池。這些...

    jayce 評(píng)論0 收藏0
  • 一張圖帶你學(xué)設(shè)計(jì)模式-單例模式

    showImg(https://segmentfault.com/img/bVbkt56?w=1078&h=1760);

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

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

0條評(píng)論

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