日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

双重检查锁模式导致空指针

發(fā)布時間:2024/10/5 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 双重检查锁模式导致空指针 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

今天遇到一個問題:莫名奇妙報了個空指針,后來發(fā)現(xiàn)原來單例模式在高并發(fā)下引起的:

雙重檢查鎖模式的一般實(shí)現(xiàn):

雙重檢查鎖模式解決了單例、性能、線程安全問題,但是這種寫法同樣存在問題:在多線程的情況下,可能會出現(xiàn)空指針問題,出現(xiàn)問題的原因是JVM在實(shí)例化對象的時候會進(jìn)行優(yōu)化和指令重排序操作。

雙重檢查鎖模式的一般實(shí)現(xiàn)

public class DoubleCheckLockMode {private static DoubleCheckLockMode instance;/*** 私有化構(gòu)造函數(shù)*/private DoubleCheckLockMode(){}/*** 提供公開獲取實(shí)例接口* @return*/public static DoubleCheckLockMode getInstance(){// 第一次判斷,如果這里為空,不進(jìn)入搶鎖階段,直接返回實(shí)例if (instance == null) {synchronized (DoubleCheckLockMode.class) {// 搶到鎖之后再次判斷是否為空if (instance == null) {instance = new DoubleCheckLockMode();}}}return instance;} }

雙重檢查鎖模式解決了單例、性能、線程安全問題,但是這種寫法同樣存在問題:在多線程的情況下,可能會出現(xiàn)空指針問題,出現(xiàn)問題的原因是JVM在實(shí)例化對象的時候會進(jìn)行優(yōu)化和指令重排序操作

什么是指令重排?

private SingletonObject(){// 第一步int x = 10;// 第二步int y = 30;// 第三步Object o = new Object(); }

上面的構(gòu)造函數(shù)SingletonObject(),JVM?會對它進(jìn)行指令重排序,所以執(zhí)行順序可能會亂掉,但是不管是那種執(zhí)行順序,JVM?最后都會保證所以實(shí)例都完成實(shí)例化。?如果構(gòu)造函數(shù)中操作比較多時,為了提升效率,JVM?會在構(gòu)造函數(shù)里面的屬性未全部完成實(shí)例化時,就返回對象。雙重檢測鎖出現(xiàn)空指針問題的原因就是出現(xiàn)在這里,當(dāng)某個線程獲取鎖進(jìn)行實(shí)例化時,其他線程就直接獲取實(shí)例使用,由于JVM指令重排序的原因,其他線程獲取的對象也許不是一個完整的對象,所以在使用實(shí)例的時候就會出現(xiàn)空指針異常問題。

雙重檢查鎖模式優(yōu)化

要解決雙重檢查鎖模式帶來空指針異常的問題,只需要使用volatile關(guān)鍵字,volatile關(guān)鍵字嚴(yán)格遵循h(huán)appens-before原則,即:在讀操作前,寫操作必須全部完成

public class DoubleCheckLockModelVolatile {/*** 添加volatile關(guān)鍵字,保證在讀操作前,寫操作必須全部完成*/private static volatile DoubleCheckLockModelVolatile instance;/*** 私有化構(gòu)造函數(shù)*/private DoubleCheckLockModelVolatile(){}/*** 提供公開獲取實(shí)例接口* @return*/public static DoubleCheckLockModelVolatile getInstance(){if (instance == null) {synchronized (DoubleCheckLockModelVolatile.class) {if (instance == null) {instance = new DoubleCheckLockModelVolatile();}}}return instance;} }

順便復(fù)習(xí)一下設(shè)計模式:

一、設(shè)計模式

1.1 設(shè)計模式是什么?

  • 設(shè)計模式是解決特定問題的一系列套路,是前輩們的代碼設(shè)計經(jīng)驗(yàn)的總結(jié),具有一定的普遍性,可以反復(fù)使用。其目的是為了提高代碼的可重用性、代碼的可讀性和代碼的可靠性。
  • 設(shè)計模式的本質(zhì)是面向?qū)ο笤O(shè)計原則的實(shí)際運(yùn)用,是對類的封裝性、繼承性和多態(tài)性以及類的關(guān)聯(lián)關(guān)系和組合關(guān)系的充分理解。
  • 1.2 為什么要使用設(shè)計模式?

    項目的需求是永遠(yuǎn)在變的,為了應(yīng)對這種變化,使得我們的代碼能夠輕易的實(shí)現(xiàn)解耦和拓展

    1.3 設(shè)計模式類型

    • 創(chuàng)建型模式

    創(chuàng)建型模式的主要關(guān)注點(diǎn)是怎樣創(chuàng)建對象,它的主要特點(diǎn)是將對象的創(chuàng)建與使用分離。這樣可以降低系統(tǒng)的耦合度,使用者不需要關(guān)注對象的創(chuàng)建細(xì)節(jié)。

    • 結(jié)構(gòu)型模式

    結(jié)構(gòu)型模式描述如何將類或?qū)ο蟀茨撤N布局組成更大的結(jié)構(gòu)。它分為類結(jié)構(gòu)型模式和對象結(jié)構(gòu)型模式,前者采用繼承機(jī)制來組織接口和類,后者釆用組合或聚合來組合對象。

    • 行為型模式

    行為型模式用于描述程序在運(yùn)行時復(fù)雜的流程控制,即描述多個類或?qū)ο笾g怎樣相互協(xié)作共同完成單個對象都無法單獨(dú)完成的任務(wù),它涉及算法與對象間職責(zé)的分配。它分為類行為模式和對象行為模式,前者采用繼承機(jī)制來在類間分派行為,后者采用組合或聚合在對象間分配行為。

    創(chuàng)建型模式結(jié)構(gòu)型模式行為型模式
    單例模式、抽象工廠模式、建造者模式、工廠模式、原型模式適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式模版方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、解釋器模式、狀態(tài)模式、策略模式、職責(zé)鏈模式(責(zé)任鏈模式)、訪問者模式

    二、面向?qū)ο笤O(shè)計的六大設(shè)計原則

    2.1 開閉原則

    一個軟件實(shí)體如類、模塊和函數(shù)應(yīng)該對擴(kuò)展開放,對修改關(guān)閉

    • 解讀
  • 用抽象構(gòu)建框架,用實(shí)現(xiàn)擴(kuò)展細(xì)節(jié);
  • 不以改動原有類的方式來實(shí)現(xiàn)新需求,而是應(yīng)該以實(shí)現(xiàn)事先抽象出來的接口(或具體類繼承抽象類)的方式來實(shí)現(xiàn)。
    • 優(yōu)點(diǎn)
  • 可以在不改動原有代碼的前提下給程序擴(kuò)展功能,增加了程序的可擴(kuò)展性;
  • 同時也降低了程序的維護(hù)成本。
  • 2.2 單一職責(zé)原則

    一個類只允許有一個職責(zé),即只有一個導(dǎo)致該類變更的原因。

    • 解讀
  • 類職責(zé)的變化往往就是導(dǎo)致類變化的原因:也就是說如果一個類具有多種職責(zé),就會有多種導(dǎo)致這個類變化的原因,從而導(dǎo)致這個類的維護(hù)變得困難;

  • 往往在軟件開發(fā)中隨著需求的不斷增加,可能會給原來的類添加一些本來不屬于它的一些職責(zé),從而違反了單一職責(zé)原則。如果我們發(fā)現(xiàn)當(dāng)前類的職責(zé)不僅僅有一個,就應(yīng)該將本來不屬于該類真正的職責(zé)分離出去

  • 不僅僅是類,函數(shù)(方法)也要遵循單一職責(zé)原則,即:一個函數(shù)(方法)只做一件事情。如果發(fā)現(xiàn)一個函數(shù)(方法)里面有不同的任務(wù),則需要將不同的任務(wù)以另一個函數(shù)(方法)的形式分離出去。

    • 優(yōu)點(diǎn)
  • 提高代碼的可讀性,更實(shí)際性地更降低了程序出錯的風(fēng)險;
  • 有利于bug的追蹤,降低了程序的維護(hù)成本。
  • 2.3 依賴倒置原則

  • 依賴抽象,而不是依賴實(shí)現(xiàn);
  • 抽象不應(yīng)該依賴細(xì)節(jié);細(xì)節(jié)應(yīng)該依賴抽象;
  • 高層模塊不能依賴低層模塊,二者都應(yīng)該依賴抽象。
    • 解讀
  • 面向接口編程,而不是面向?qū)崿F(xiàn)編程;
  • 盡量不要從具體的類派生,而是以繼承抽象類或?qū)崿F(xiàn)接口來實(shí)現(xiàn);
  • 關(guān)于高層模塊與低層模塊的劃分可以按照決策能力的高低進(jìn)行劃分。業(yè)務(wù)層自然就處于上層模塊,邏輯層和數(shù)據(jù)層自然就歸類為底層。
    • 優(yōu)點(diǎn)
  • 通過抽象來搭建框架,建立類和類的關(guān)聯(lián),以減少類間的耦合性;
  • 以抽象搭建的系統(tǒng)要比以具體實(shí)現(xiàn)搭建的系統(tǒng)更加穩(wěn)定,擴(kuò)展性更高,同時也便于維護(hù)。
    • 里氏替換原則

    子類可以擴(kuò)展父類的功能,但不能改變父類原有的功能。也就是說,子類繼承父類時,除添加新的方法完成新增功能外,盡量不要重寫父類的方法。

    2.4 接口隔離原則

    多個特定的客戶端接口要好于一個通用性的總接口。

    • 解讀
  • 客戶端不應(yīng)該依賴它不需要實(shí)現(xiàn)的接口;
  • 不建立龐大臃腫的接口,應(yīng)盡量細(xì)化接口,接口中的方法應(yīng)該盡量少。
  • 注意:接口的粒度也不能太小。如果過小,則會造成接口數(shù)量過多,使設(shè)計復(fù)雜化。

    • 優(yōu)點(diǎn)

    避免同一個接口里面包含不同類職責(zé)的方法,接口責(zé)任劃分更加明確,符合高內(nèi)聚低耦合的思想。

    2.5 迪米特法則(最少知道原則)

    一個對象應(yīng)該對盡可能少的對象有接觸,也就是只接觸那些真正需要接觸的對象。

    • 解讀

    一個類應(yīng)該只和它的成員變量,方法的輸入,返回參數(shù)中的類作交流,而不應(yīng)該引入其他的類(間接交流)。

    • 優(yōu)點(diǎn)

    可以良好地降低類與類之間的耦合,減少類與類之間的關(guān)聯(lián)程度,讓類與類之間的協(xié)作更加直接。

    2.6 組合聚合復(fù)用原則

    所有引用基類的地方必須能透明地使用其子類的對象,也就是說子類對象可以替換其父類對象,而程序執(zhí)行效果不變。

    -解讀

    在繼承體系中,子類中可以增加自己特有的方法,也可以實(shí)現(xiàn)父類的抽象方法,但是不能重寫父類的非抽象方法,否則該繼承關(guān)系就不是一個正確的繼承關(guān)系。

    • 優(yōu)點(diǎn)

    可以檢驗(yàn)繼承使用的正確性,約束繼承在使用上的泛濫。

    關(guān)于設(shè)計模式更多的可以參考:

    菜鳥教程:https://www.runoob.com/design-pattern/singleton-pattern.html

    部分摘自:https://www.cnblogs.com/vandusty/p/11444293.html

    ?

    ?

    ?

    ?

    ?

    總結(jié)

    以上是生活随笔為你收集整理的双重检查锁模式导致空指针的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。