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

java 單例模式容易忽略的細(xì)節(jié)

 更新時(shí)間:2020年12月24日 11:38:53   作者:努力的老劉  
這篇文章主要介紹了java 單例模式容易忽略的細(xì)節(jié),幫助大家更好的理解和使用java 單例模式,感興趣的朋友可以了解下

java單例模式

直接講實(shí)現(xiàn)單例模式的兩種方法:懶漢式和餓漢式,單例模式的概念自己上網(wǎng)搜吧這里就不講了!

這里會(huì)涉及到j(luò)ava中的jvm,如果你沒(méi)有這方面的知識(shí),我建議你先去補(bǔ)補(bǔ),不然會(huì)有點(diǎn)迷糊!

首先說(shuō)說(shuō)類(lèi)什么時(shí)候進(jìn)行加載?

java虛擬機(jī)沒(méi)有進(jìn)行強(qiáng)制性的約束,但是對(duì)于初始化卻嚴(yán)格規(guī)定了有且只有4種情況必須先對(duì)類(lèi)進(jìn)行初始化。

我們要知道的是在類(lèi)加載的過(guò)程中,加載、驗(yàn)證、準(zhǔn)備是在初始化之前完成的,所以進(jìn)行了初始化,加載、驗(yàn)證、準(zhǔn)備自然就在之前完成了。

然后這四種情況是分別遇到 new 、 getstatic 、 putstatic 和 invokestatic 這四條指令時(shí),如果對(duì)應(yīng)的類(lèi)沒(méi)有初始化,則要對(duì)對(duì)應(yīng)的類(lèi)先進(jìn)行初始化。

講完類(lèi)加載時(shí)機(jī),就可以講懶漢式和餓漢式了。

直接先說(shuō)說(shuō)懶漢式為什么是線(xiàn)程不安全的?

先看最開(kāi)始的代碼:

public class Student2 {

  //1:構(gòu)造私有
  private Student2(){}
  //2:定義私有靜態(tài)成員變量,先不初始化
  private static Student2 student = null;

  //3:定義公開(kāi)靜態(tài)方法,獲取本身對(duì)象
  public static Student2 getSingletonInstance(){
    //沒(méi)有對(duì)象,再去創(chuàng)建
    if (student == null) {
      student = new Student2();
    }
    //有對(duì)象就返回已有對(duì)象
    return student;
  }  
}

結(jié)合之前講的類(lèi)加載內(nèi)容,遇到new或加載靜態(tài)方法了就會(huì)進(jìn)行類(lèi)加載了。

線(xiàn)程1它new了一個(gè)對(duì)象,線(xiàn)程2它緊接著也new一個(gè)對(duì)象,第二個(gè)對(duì)象的值把第一個(gè)對(duì)象的值覆蓋了,不管new了多少個(gè)對(duì)象,都會(huì)產(chǎn)生垃圾對(duì)象,只有最后一個(gè)對(duì)象才會(huì)保持住,其他對(duì)象都會(huì)變成不可達(dá)對(duì)象,被垃圾回收,這個(gè)過(guò)程就相當(dāng)于產(chǎn)生了大量無(wú)效對(duì)象,這就是線(xiàn)程不安全的原因!

那為了讓?xiě)袧h式變得線(xiàn)程安全,我們要怎么做?

看代碼:

public class Student4 {

  private volatile static Student4 student = null;
  private Student4() {}

  public static Student4 getSingletonInstance() {
    if (student == null) {//第一個(gè)null判斷,是先大范圍過(guò)濾一遍
      synchronized (Student4.class) {
        if (student == null) {
          student = new Student4();
        }
      }
    }
    return student;
  }
}

這個(gè)叫雙重檢查鎖DCL,第一個(gè)if先大范圍判斷是不是空值,經(jīng)過(guò)synchronized,線(xiàn)程1先進(jìn)去執(zhí)行完后,線(xiàn)程2才能進(jìn)去,然后第二個(gè)if判斷是否完成創(chuàng)建類(lèi)的實(shí)例,線(xiàn)程1創(chuàng)建完了,線(xiàn)程2就不用創(chuàng)建了。

那為什么要加volatile關(guān)鍵字呢?

因?yàn)槲覀僑tudent student = new Student()的執(zhí)行過(guò)程是:

1、new觸發(fā)類(lèi)加載機(jī)制(已經(jīng)被加載過(guò)的類(lèi)不需要再次加載)

2、分配內(nèi)存空間

3、將對(duì)象進(jìn)行初始化4、講對(duì)象引用地址賦值給??臻g中的變量但我們JVM中的JIT即時(shí)編輯器會(huì)對(duì)代碼的執(zhí)行過(guò)程進(jìn)行優(yōu)化,把過(guò)程變?yōu)?、2、4、3。

這是什么意思呢?就是未經(jīng)初始化直接賦值,這樣就是student直接有值了,但整個(gè)對(duì)象還未初始化完成,所以這個(gè)對(duì)象是不完整的,是個(gè)未成品。在JVM規(guī)范中,它是一個(gè)根本不能用的對(duì)象。

到了這個(gè)時(shí)候,線(xiàn)程1做了這么多事,我們讓它休息會(huì),給CPU稍微停一下,線(xiàn)程2就來(lái)了,它就直接得到了對(duì)象,但它調(diào)用對(duì)象的方法時(shí),就會(huì)報(bào)錯(cuò)。雖然這個(gè)對(duì)象有值,但還未初始化完成。所以我們要加上volatile關(guān)鍵字禁止指令重新排序。

面試重災(zāi)區(qū)說(shuō)的差不多了,餓漢式還是要講講。

最后就說(shuō)說(shuō)餓漢式為什么沒(méi)有線(xiàn)程安全問(wèn)題?​

看代碼:

public class Student1 {
  // 2:成員變量初始化本身對(duì)象
  private static Student1 student = new Student1();
  // 構(gòu)造私有
  private Student1() {
  }
  // 3:對(duì)外提供公共方法獲取對(duì)象
  public static Student1 getSingletonInstance() {
    return student;
  }
  public void sayHello(String name) {
    System.out.println("hello," + name);
  }
}

根據(jù)類(lèi)加載的東西,在多線(xiàn)程的條件下,線(xiàn)程1先執(zhí)行g(shù)etSingletonInstance()時(shí),就會(huì)進(jìn)行類(lèi)加載,類(lèi)的靜態(tài)資源就會(huì)進(jìn)行初始化。根據(jù)JVM安全機(jī)制里說(shuō)的,當(dāng)一個(gè)類(lèi)被JVM加載的時(shí)候,該類(lèi)的加載是線(xiàn)程安全的,相當(dāng)于JVM對(duì)該過(guò)程加鎖了。所以整個(gè)過(guò)程處于一個(gè)鎖的范圍內(nèi),然后靜態(tài)成員變量進(jìn)行初始化就相當(dāng)于Student1()被new了,只會(huì)被new一次。

當(dāng)?shù)诙€(gè)線(xiàn)程進(jìn)來(lái),它就發(fā)現(xiàn)這個(gè)類(lèi)已經(jīng)被加載了,就不需要進(jìn)行加載了,對(duì)象也不需要頻繁創(chuàng)建,所以線(xiàn)程是安全的!

總結(jié)

老劉看過(guò)很多關(guān)于java單例模式的資料,多多少少都會(huì)缺少一點(diǎn)細(xì)節(jié),這次老劉把它補(bǔ)全了。

最后,如果覺(jué)得有哪里寫(xiě)的不好或者有錯(cuò)誤的地方,可以聯(lián)系公眾號(hào):努力的老劉,進(jìn)行交流。

如果覺(jué)得寫(xiě)的不錯(cuò),給老劉點(diǎn)個(gè)贊!

以上就是java 單例模式容易忽略的細(xì)節(jié)的詳細(xì)內(nèi)容,更多關(guān)于java 單例模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 踩坑批量更新sql報(bào)錯(cuò),實(shí)際sql能夠正常執(zhí)行的問(wèn)題

    踩坑批量更新sql報(bào)錯(cuò),實(shí)際sql能夠正常執(zhí)行的問(wèn)題

    在項(xiàng)目工程遷移過(guò)程中,遇到了一個(gè)批量更新接口在新工程中報(bào)錯(cuò)的問(wèn)題,通過(guò)分析,排除了代碼錯(cuò)誤的可能,最終發(fā)現(xiàn)是由于數(shù)據(jù)庫(kù)連接配置不當(dāng)導(dǎo)致的,在jdbc連接字符串中加入allowMultiQueries=true參數(shù)后,問(wèn)題得以解決,這個(gè)參數(shù)的作用是允許SQL批量執(zhí)行
    2022-12-12
  • Java C++題解leetcode 1684統(tǒng)計(jì)一致字符串的數(shù)目示例

    Java C++題解leetcode 1684統(tǒng)計(jì)一致字符串的數(shù)目示例

    這篇文章主要為大家介紹了Java C++題解leetcode 1684統(tǒng)計(jì)一致字符串的數(shù)目示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • MyBatis數(shù)據(jù)脫敏的實(shí)現(xiàn)方案介紹

    MyBatis數(shù)據(jù)脫敏的實(shí)現(xiàn)方案介紹

    在我們數(shù)據(jù)庫(kù)中有些時(shí)候會(huì)保存一些用戶(hù)的敏感信息,比如:手機(jī)號(hào)、銀行卡等信息,如果這些信息以明文的方式保存,那么是不安全的
    2022-08-08
  • Eureka源碼核心類(lèi)預(yù)備知識(shí)

    Eureka源碼核心類(lèi)預(yù)備知識(shí)

    這篇文章主要為大家介紹了Eureka源碼核心類(lèi)預(yù)備知識(shí)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • IDEA遠(yuǎn)程管理docker鏡像及容器服務(wù)的實(shí)現(xiàn)

    IDEA遠(yuǎn)程管理docker鏡像及容器服務(wù)的實(shí)現(xiàn)

    本文主要介紹了IDEA遠(yuǎn)程管理docker鏡像及容器服務(wù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • 全面了解Java中Native關(guān)鍵字的作用

    全面了解Java中Native關(guān)鍵字的作用

    下面小編就為大家?guī)?lái)一篇全面了解Java中Native關(guān)鍵字的作用。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-07-07
  • SpringBoot動(dòng)態(tài)定時(shí)任務(wù)實(shí)現(xiàn)完整版

    SpringBoot動(dòng)態(tài)定時(shí)任務(wù)實(shí)現(xiàn)完整版

    最近有幸要開(kāi)發(fā)個(gè)動(dòng)態(tài)定時(shí)任務(wù),這里簡(jiǎn)單再梳理一下,下面這篇文章主要給大家介紹了關(guān)于SpringBoot動(dòng)態(tài)定時(shí)任務(wù)實(shí)現(xiàn)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-02-02
  • 詳解eclipse創(chuàng)建maven項(xiàng)目實(shí)現(xiàn)動(dòng)態(tài)web工程完整示例

    詳解eclipse創(chuàng)建maven項(xiàng)目實(shí)現(xiàn)動(dòng)態(tài)web工程完整示例

    這篇文章主要介紹了詳解eclipse創(chuàng)建maven項(xiàng)目實(shí)現(xiàn)動(dòng)態(tài)web工程完整示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • 教你怎么用Java開(kāi)發(fā)掃雷游戲

    教你怎么用Java開(kāi)發(fā)掃雷游戲

    我們那時(shí)候上機(jī)經(jīng)常玩掃雷,試想如果我當(dāng)年可以用 java 寫(xiě)個(gè)掃雷出來(lái),那場(chǎng)面不用我多說(shuō)了吧,大家讓開(kāi),我要開(kāi)始裝逼了,之前用JavaScript寫(xiě)過(guò)了一個(gè)掃雷,這次我用java再寫(xiě)了一遍,權(quán)當(dāng)是復(fù)習(xí)咯.文中有非常詳細(xì)的代碼示例,需要的朋友可以參考下
    2021-05-05
  • Java實(shí)現(xiàn)選擇排序算法的實(shí)例教程

    Java實(shí)現(xiàn)選擇排序算法的實(shí)例教程

    這篇文章主要介紹了Java實(shí)現(xiàn)選擇排序算法的實(shí)例教程,選擇排序的時(shí)間復(fù)雜度為О(n²),需要的朋友可以參考下
    2016-05-05

最新評(píng)論