最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺談java 單例模式DCL的缺陷及單例的正確寫法

 更新時(shí)間:2020年09月28日 10:08:42   作者:帶你裝逼帶你飛的程序猿  
這篇文章主要介紹了淺談java 單例模式DCL的缺陷及單例的正確寫法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧

1 前言

單例模式是我們經(jīng)常使用的一種模式,一般來(lái)說(shuō)很多資料都建議我們寫成如下的模式:

/**
 * Created by qiyei2015 on 2017/5/13.
 */
public class Instance {
  private String str = "";
  private int a = 0;

  private static Instance ins = null;
  /**
   * 構(gòu)造方法私有化
   */
  private Instance(){
    str = "hello";
    a = 20;
  }
  
  /**
   * DCL方式獲取單例
   * @return
   */
  public static Instance getInstance(){
    if (ins == null){
      synchronized (Instance.class){
        if (ins == null){
          ins = new Instance();
        }
      }
    }
    return ins;
  }  
}

但是這種方式其實(shí)是有缺陷的,具體什么缺陷呢?我們首先要了解JVM了內(nèi)存模型,請(qǐng)看下面分析

2 JVM內(nèi)存模型

JVM模型如下圖:

這里著重介紹下VM Stack,其他的我相信都比較熟悉。

VM Stack是線程私有的區(qū)域。他是java方法執(zhí)行時(shí)的字典:它里面記錄了局部變量表、 操作數(shù)棧、 動(dòng)態(tài)鏈接、 方法出口等信息。

在《java虛擬機(jī)規(guī)范》一書中對(duì)這部分的描述如下:

棧幀( Frame)是用來(lái)存儲(chǔ)數(shù)據(jù)和部分過(guò)程結(jié)果的數(shù)據(jù)結(jié)構(gòu),同時(shí)也被用來(lái)處理動(dòng)態(tài)鏈接 (Dynamic Linking)、 方法返回值和異常分派( Dispatch Exception)。

棧幀隨著方法調(diào)用而創(chuàng)建,隨著方法結(jié)束而銷毀——無(wú)論方法是正常完成還是異常完成(拋出了在方法內(nèi)未被捕獲的異常)都算作方法結(jié)束。

棧幀的存儲(chǔ)空間分配在 Java 虛擬機(jī)棧( §2.5.5)之中,每一個(gè)棧幀都有自己的局部變量表( Local Variables, §2.6.1)、操作數(shù)棧( OperandStack, §2.6.2)和指向當(dāng)前方法所屬的類的運(yùn)行時(shí)常量池( §2.5.5)的引用。

java中某個(gè)線程在訪問(wèn)堆中的線程共享變量時(shí),為了加快訪問(wèn)速度,提升效率,會(huì)把該變量臨時(shí)拷貝一份到自己的VM Stack中,并保持和堆中數(shù)據(jù)的同步。

3 傳統(tǒng)DCL方式的缺陷

有了以上的基礎(chǔ)知識(shí)我們就可以知道DCL方式的缺陷在哪兒了。當(dāng)線程A在獲取了Instance.class鎖時(shí),對(duì)ins進(jìn)行 ins = new Instance() 初始化時(shí),由于這是很多條指令,jvm可能會(huì)亂序執(zhí)行。

這個(gè)時(shí)候如果線程B在執(zhí)行if (ins == null)時(shí),正常情況下,如果為true,說(shuō)明需要獲取Instance.class鎖,等待初始化。

但是這時(shí)候,假設(shè)線程A再?zèng)]有對(duì)ins進(jìn)行初始化完,比如只對(duì)str進(jìn)行了賦值,還沒(méi)有來(lái)的及對(duì)a進(jìn)行賦值,假如jvm將未完成賦值的值拷貝回堆中,這個(gè)時(shí)候線程B有可能讀到的值就不是為null了,就會(huì)造成數(shù)據(jù)丟失的情況。這時(shí)候我們發(fā)現(xiàn)線程B獲取的對(duì)象中a的值是0,而不是20

因?yàn)椋簩?duì)ins的寫操作不 happen-before 對(duì)它的讀操作

這就是DCL方式的缺陷,那么怎么避免呢?首先我們需要了解分析多線程的一大利器

4 happen-before原則

Happen-Before規(guī)則:

1 同一個(gè)線程中,書寫在前面的操作happen-before書寫在后面的操作。這條規(guī)則是說(shuō),在單線程 中操作間happen-before關(guān)系完全是由源代碼的順序決定的,這里的前提“在同一個(gè)線程中”是很重要的,這條規(guī)則也稱為單線程規(guī)則 。

這個(gè)規(guī)則多少說(shuō)得有些簡(jiǎn)單了,考慮到控制結(jié)構(gòu)和循環(huán)結(jié)構(gòu),書寫在后面的操作可能happen-before書寫在前面的操作,不過(guò)我想讀者應(yīng)該明白我的意思。

2 對(duì)鎖的unlock操作happen-before后續(xù)的對(duì)同一個(gè)鎖的lock操作。這里的“后續(xù)”指的是時(shí)間上的先后關(guān)系,unlock操作發(fā)生在退出同步塊之后,lock操作發(fā)生在進(jìn)入同步塊之前。這是條最關(guān)鍵性的規(guī)則,線程安全性主要依賴于這條規(guī)則。

但是僅僅是這條規(guī)則仍然不起任何作用,它必須和下面這條規(guī)則聯(lián)合起來(lái)使用才顯得意義重大。這里關(guān)鍵條件是必須對(duì)“同一個(gè)鎖”的lock和unlock。

如果操作A happen-before操作B,操作B happen-before操作C,那么操作A happen-before操作C。這條規(guī)則也稱為傳遞規(guī)

3 對(duì)volatile字段的寫操作happen-before后續(xù)的對(duì)同一個(gè)字段的讀操作.(Java5 新增)

4 單例模式的正確寫法

有了以上的分析我們知道,我們只需要在保證對(duì)ins的訪問(wèn)是讀在寫之后即可,因此正確的做法是在ins 前加上一個(gè)關(guān)鍵字volatile。因此DCL的正確寫法應(yīng)該如下:

/**
 * Created by qiyei2015 on 2017/5/13.
 */
public class Instance {
  private String str = "";
  private int a = 0;

  private volatile static Instance ins = null;
  /**
   * 構(gòu)造方法私有化
   */
  private Instance(){
    str = "hello";
    a = 20;
  }

  /**
   * DCL方式獲取單例
   * @return
   */
  public static Instance getInstance(){
    if (ins == null){
      synchronized (Instance.class){
        if (ins == null){
          ins = new Instance();
        }
      }
    }
    return ins;
  }
}

其實(shí)單例模式也有另一種我很喜歡的寫法,那就是內(nèi)部類:

/**
 * Created by qiyei2015 on 2017/5/13.
 */
public class Instance {

  /**
   * 構(gòu)造方法私有化
   */
  private Instance(){
  }
  
  private static class SingleHolder{
    private static final Instance ins = new Instance();
  }

  /**
   * 內(nèi)部類方式獲取單例
   * @return
   */
  public static Instance getInstance(){
    return SingleHolder.ins;
  }  
}

這種從jvm虛擬機(jī)上保證了單例,并且也是懶式加載。

以上這篇淺談java 單例模式DCL的缺陷及單例的正確寫法就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot中的RabbitMQ用法詳解

    SpringBoot中的RabbitMQ用法詳解

    RabbitMQ是一個(gè)開(kāi)源的消息隊(duì)列系統(tǒng),它通過(guò)AMQP(高級(jí)消息隊(duì)列協(xié)議)來(lái)實(shí)現(xiàn)消息的傳遞,SpringBoot是目前非常流行的Java開(kāi)發(fā)框架,它提供了很多便利性的功能,其中就包括對(duì)RabbitMQ的支持,在本文中,我們將介紹如何在SpringBoot中使用RabbitMQ
    2023-07-07
  • Java如何主動(dòng)從當(dāng)前線程獲取異常信息

    Java如何主動(dòng)從當(dāng)前線程獲取異常信息

    這篇文章主要介紹了Java如何主動(dòng)從當(dāng)前線程獲取異常信息,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • Java中ModelMapper?的高級(jí)使用

    Java中ModelMapper?的高級(jí)使用

    本文主要介紹了Java中ModelMapper?的高級(jí)使用,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • SpringCloud負(fù)載均衡實(shí)現(xiàn)定向路由詳情

    SpringCloud負(fù)載均衡實(shí)現(xiàn)定向路由詳情

    這篇文章主要介紹了SpringCloud負(fù)載均衡實(shí)現(xiàn)定向路由詳情,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • 身份證號(hào)碼驗(yàn)證算法深入研究和Java實(shí)現(xiàn)

    身份證號(hào)碼驗(yàn)證算法深入研究和Java實(shí)現(xiàn)

    這篇文章主要介紹了身份證號(hào)碼驗(yàn)證算法深入研究和Java實(shí)現(xiàn),本文講解了18身份證號(hào)碼的結(jié)構(gòu)、根據(jù)17位數(shù)字本體碼獲取最后一位校驗(yàn)碼程序?qū)嵗葍?nèi)容,需要的朋友可以參考下
    2015-06-06
  • java DOM4J 讀取XML實(shí)例代碼

    java DOM4J 讀取XML實(shí)例代碼

    最近學(xué)習(xí)Java,在處理XML文檔的時(shí)候,查閱相關(guān)資料,發(fā)現(xiàn)了DOM4J這個(gè)jre庫(kù),相對(duì)C#的XML處理來(lái)說(shuō),功能還算是跟得
    2013-09-09
  • Java基礎(chǔ)之方法重寫詳解

    Java基礎(chǔ)之方法重寫詳解

    這篇文章主要介紹了Java基礎(chǔ)之方法重寫詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-05-05
  • Java中實(shí)體與Map之間的相互轉(zhuǎn)換代碼示例

    Java中實(shí)體與Map之間的相互轉(zhuǎn)換代碼示例

    生活中經(jīng)常用到map數(shù)據(jù)與實(shí)體類的轉(zhuǎn)換,下面這篇文章主要給大家介紹了關(guān)于Java中實(shí)體與Map之間相互轉(zhuǎn)換的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12
  • Spring Hibernate實(shí)現(xiàn)分頁(yè)功能

    Spring Hibernate實(shí)現(xiàn)分頁(yè)功能

    這篇文章主要為大家詳細(xì)介紹了Spring Hibernate實(shí)現(xiàn)分頁(yè)功能的相關(guān)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • AsyncHttpClient KeepAliveStrategy源碼流程解讀

    AsyncHttpClient KeepAliveStrategy源碼流程解讀

    這篇文章主要為大家介紹了AsyncHttpClient KeepAliveStrategy源碼流程解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12

最新評(píng)論

商河县| 太仆寺旗| 新化县| 旬邑县| 和林格尔县| 长治市| 招远市| 左贡县| 合山市| 盐池县| 石嘴山市| 芦溪县| 湘乡市| 靖西县| 集安市| 莱州市| 石景山区| 甘孜县| 阿克苏市| 永川市| 大新县| 乐陵市| 天全县| 石嘴山市| 唐山市| 会昌县| 哈密市| 云安县| 绍兴县| 濉溪县| 拉孜县| 沙湾县| 如皋市| 兴安盟| 常熟市| 高淳县| 天峻县| 岚皋县| 荆门市| 南宫市| 灵璧县|