欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java多線程案例之單例模式懶漢+餓漢+枚舉

 更新時間:2022年06月02日 14:27:20   作者:??未見花聞????  
這篇文章主要介紹了Java多線程案例之單例模式懶漢+餓漢+枚舉,文章著重介紹在多線程的背景下簡單的實現(xiàn)單例模式,需要的小伙伴可以參考一下

前言:

本篇文章將介紹Java多線程中的幾個典型案例之單例模式,所謂單例模式,就是一個類只有一個實例對象,本文將著重介紹在多線程的背景下,單例模式的簡單實現(xiàn)。

1.單例模式概述

單例模式,是一種常用的軟件設(shè)計模式。在它的核心結(jié)構(gòu)中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統(tǒng)中,應(yīng)用該模式的類一個類只有一個實例,即一個類只有一個對象實例。

單例模式有兩種典型的實現(xiàn),一是餓漢模式,二是懶漢模式,餓漢模式中的“餓”并不是真的表示“餓”,更加準確的來說應(yīng)該是表示“急”,那就是一個單例類還沒被使用,它的單例對象就已經(jīng)創(chuàng)建好了,而懶漢模式,要等到使用這個單例類時才創(chuàng)建單例對象。

單例模式中的單例類,只能擁有一個實例對象,又static修飾的成員是屬于類的,也就是只有一份,所以我們可以使用static修飾的成員變量保存實例對象的引用。

2.單例模式的簡單實現(xiàn)

2.1餓漢模式

由于單例模式中,一個類只能擁有一個實例對象,所以需要將類構(gòu)造方法封裝,防止類被創(chuàng)建多個實例對象,但是在使用該類時必須要得到該類的實例對象,因此我們得創(chuàng)建一個獲取該唯一實例對象的方法getInstance。

而對于該類的實例對象,在類中我們可以使用屬于類的成員變量來保存(即static成員變量)。

//單例模式 - 餓漢模式
class HungrySingleton {
    //1.使用一個變量來保存該類唯一的實例,因為單例模式在一個程序中只能擁有一個實例,由于static成員只有一份,我們可以使用static變量來保存
    private static final HungrySingleton instance = new HungrySingleton();

    //2.封裝構(gòu)造方法,防止該類被實例出新的對象
    private HungrySingleton() {}

    //3.獲取該類的唯一實例對象
    public HungrySingleton getInstance() {
        return instance;
    }
}

多線程情況下,對于上述簡單實現(xiàn)的餓漢式單例模式,只需要考慮getInstance方法是否線程安全即可,由于該方法就一句返回語句,即一次讀操作,而讀操作是線程安全的,所以getInstance方法也就是線程安全的,綜上餓漢式單例模式是線程安全的。

2.2懶漢模式

懶漢模式相比于餓漢模式,區(qū)別就是實例對象創(chuàng)建時機不同,懶漢模式需要等到第一次使用時才創(chuàng)建實例對象,所以僅僅只需要修改獲取對象的方法即可。

不考慮多線程情況,懶漢模式實現(xiàn)代碼如下:

//單例模式 - 懶漢模式
class SlackerSingleton {
    //1.使用一個變量來保存該類唯一的實例,因為單例模式在一個程序中只能擁有一個實例,由于static成員只有一份,我們可以使用static變量來保存
    //懶漢單例模式是在使用的時候創(chuàng)建對象,因此初始時對象不應(yīng)該被創(chuàng)建
    private static SlackerSingleton instance;

    //2.封裝構(gòu)造方法,防止該類被實例出新的對象
    private SlackerSingleton() {}

    //3.獲取該類的唯一對象,如果沒有就創(chuàng)建
    public SlackerSingleton getInstance() {
        if (instance == null) {
            instance = new SlackerSingleton();
        }
        return instance;
    }
}

多線程情況下,由于getInstance方法中存在兩次讀(一次判斷一次返回)操作一次寫操作(修改intsance變量的值),instance變量為初始化時(即instance=null)可能會存在多個線程進入判斷語句,這樣該類可能會被實例出多個對象,所以上述實現(xiàn)的懶漢式單例模式是線程不安全的。

造成線程不安全的代碼段為if語句里面的讀操作和instance的修改操作,所以我們需要對這段代碼進行加鎖,然后就得到了線程安全的懶漢模式:

//多線程情況下餓漢模式獲取對象時只讀不修改,所以是線程安全的
//多線程情況下懶漢模式獲取對象時存在兩次讀操作,分別為判斷instance是否為null和返回instance,除了讀操作還存在修改操作,即新建對象并使instance指向該對象
//懶漢模式對象還未初始化的時候,可能會存在多個線程進入判斷語句,會導(dǎo)致實例出多個對象,因此懶漢單例模式是線程不安全的。

//線程安全單例模式 - 懶漢模式
class SafeSlackerSingleton {
    //1.使用一個變量來保存該類唯一的實例,因為單例模式在一個程序中只能擁有一個實例,由于static成員只有一份,我們可以使用static變量來保存
    //懶漢單例模式是在使用的時候創(chuàng)建對象,因此初始時對象不應(yīng)該被創(chuàng)建
    private static SafeSlackerSingleton instance;

    //2.封裝構(gòu)造方法,防止該類被實例出新的對象
    private SafeSlackerSingleton() {}

    //3.獲取該類的唯一對象,如果沒有就創(chuàng)建
    public SafeSlackerSingleton getInstance() {
        synchronized (SafeSlackerSingleton.class) {
            if (instance == null) {
                instance = new SafeSlackerSingleton();
            }
        }
        return instance;
    }
}

但是!上述線程安全問題只出現(xiàn)在instance沒有初始化的時候,如果instance已經(jīng)初始化了,那個判斷語句就是個擺設(shè),就和餓漢模式一樣,就是線程安全的了,如果按照上面的代碼處理線程安全問題,不論instance是否已經(jīng)初始化,都要進行加鎖,因此會使鎖競爭加劇,消耗沒有必要消耗的資源,所以在加鎖前需要先判斷一下instance是否已經(jīng)初始化,如果為初始化就進行加鎖。

按照上述方案得到以下關(guān)于獲取對象的方法代碼:

    public SafeSlackerSingletonPlus getInstance() {
        //判斷instance是否初始化,如果已經(jīng)初始化了,那么該方法只有兩個讀操作,本身就是線程安全的,不需要加鎖了,這樣能減少鎖競爭,提高效率
        if (instance == null) {
            synchronized (SafeSlackerSingletonPlus.class) {
                if (instance == null) {
                    instance = new SafeSlackerSingletonPlus();
                }
            }
        }
        return instance;
    }

到這里線程安全的問題是解決了,但是別忘了編譯器它是不信任你的,它會對你寫的代碼進行優(yōu)化! 上面所寫的代碼需要判斷instance==null,而多線程情況下,很可能頻繁進行判斷,這時候線程不會去讀內(nèi)存中的數(shù)據(jù),而會直接去寄存器讀數(shù)據(jù),這時候instance值變化時,線程完全感知不到!造成內(nèi)存可見性問題,為了解決該問題需要使用關(guān)鍵字volatile修飾instance變量,防止編譯器優(yōu)化,從而保證內(nèi)存可見性。

//線程安全優(yōu)化單例模式 - 懶漢模式
class SafeSlackerSingletonPlus {
    //1.使用一個變量來保存該類唯一的實例,因為單例模式在一個程序中只能擁有一個實例,由于static成員只有一份,我們可以使用static變量來保存
    //懶漢單例模式是在使用的時候創(chuàng)建對象,因此初始時對象不應(yīng)該被創(chuàng)建
    private static volatile SafeSlackerSingletonPlus instance;

    //2.封裝構(gòu)造方法,防止該類被實例出新的對象
    private SafeSlackerSingletonPlus() {}

    //3.獲取該類的唯一對象,如果沒有就創(chuàng)建
    public SafeSlackerSingletonPlus getInstance() {
        //判斷instance是否初始化,如果已經(jīng)初始化了,那么該方法只有兩個讀操作,本身就是線程安全的,不需要加鎖了,這樣能減少鎖競爭,提高效率
        //如果線程很多,頻繁進行外層或內(nèi)層if判斷,可能會引發(fā)內(nèi)層可見性問題,因此要給instan變量加上volatile
        if (instance == null) {
            synchronized (SafeSlackerSingletonPlus.class) {
                if (instance == null) {
                    instance = new SafeSlackerSingletonPlus();
                }
            }
        }
        return instance;
    }
}

2.3枚舉實現(xiàn)單例模式

除了使用餓漢和懶漢模式還可以使用枚舉的方式實現(xiàn),在《Effective Java》書中有這樣一句話:單元素的枚舉類型已經(jīng)成為實現(xiàn)Singleton的最佳方法。 因為枚舉就是一個天然的單例,并且枚舉類型通過反射都無法獲取封裝的私有變量,非常安全。

//單元素的枚舉類型已經(jīng)成為實現(xiàn)Singleton的最佳方法
enum  EnumSingleton {
    INSTANCE;
    public void doSomething() {
        System.out.println("完成一些任務(wù)!");
    }
}

使用方式:

public class Singleton {
    public static void main(String[] args) {
        EnumSingleton.INSTANCE.doSomething();
    }
}

運行結(jié)果:

好了,有關(guān)多線程單例模式問題就討論到這里了,你學會了嗎?

到此這篇關(guān)于Java多線程案例之單例模式懶漢+餓漢+枚舉的文章就介紹到這了,更多相關(guān)Java單例模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Java編程中static關(guān)鍵字和final關(guān)鍵字的使用

    詳解Java編程中static關(guān)鍵字和final關(guān)鍵字的使用

    這篇文章主要介紹了詳解Java編程中static關(guān)鍵字和final關(guān)鍵字的使用,是Java入門學習中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-09-09
  • java設(shè)計模式之橋接模式(Bridge)

    java設(shè)計模式之橋接模式(Bridge)

    這篇文章主要為大家詳細介紹了java設(shè)計模式之橋接模式Bridge,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • SpringBoot中的自動配置原理詳解

    SpringBoot中的自動配置原理詳解

    這篇文章主要介紹了SpringBoot中的自動配置原理詳解,springboot的自動配置類直觀的表現(xiàn)就是通過一系列的注解,使得springboot項目在啟動的時候從配置文件中加載需要自動配置的類,注入容器中,需要的朋友可以參考下
    2024-01-01
  • Redisson 分布式延時隊列 RedissonDelayedQueue 運行流程

    Redisson 分布式延時隊列 RedissonDelayedQueue 運行流程

    這篇文章主要介紹了Redisson分布式延時隊列 RedissonDelayedQueue運行流程,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-09-09
  • SWT(JFace)體驗之StyledText類

    SWT(JFace)體驗之StyledText類

    有的時候Text需要實現(xiàn)這種那種的樣式。先提供在不使用StyledText類的情況:
    2009-06-06
  • java如何獲得redis所有的key-value

    java如何獲得redis所有的key-value

    這篇文章主要介紹了java如何獲得redis所有的key-value,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Springboot+Redis實現(xiàn)API接口限流的示例代碼

    Springboot+Redis實現(xiàn)API接口限流的示例代碼

    本文主要介紹了Springboot+Redis實現(xiàn)API接口限流的示例代碼,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • Java集合類中文介紹

    Java集合類中文介紹

    本文首先對Java集合類框架做了簡單說明,之后對主要類和為API做了介紹:Collection、List、Set、AbstractCollection、AbstractList、AbstractSet、Iterator、ListIterator。
    2013-11-11
  • Java Swing組件布局管理器之FlowLayout(流式布局)入門教程

    Java Swing組件布局管理器之FlowLayout(流式布局)入門教程

    這篇文章主要介紹了Java Swing組件布局管理器之FlowLayout(流式布局),結(jié)合實例形式分析了Swing組件布局管理器FlowLayout流式布局的常用方法及相關(guān)使用技巧,需要的朋友可以參考下
    2017-11-11
  • 如何將eclipse項目導(dǎo)入到idea的方法步驟(圖文)

    如何將eclipse項目導(dǎo)入到idea的方法步驟(圖文)

    這篇文章主要介紹了如何將eclipse項目導(dǎo)入到idea的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-03-03

最新評論