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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

单例模式中的线程安全问题

發布時間:2023/12/10 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 单例模式中的线程安全问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

🍈一. 使用多線程需要考慮的因素

🍊二. 單例模式

🌴1. 餓漢模式

🌾2. 懶漢模式

🌵3. 懶漢模式(使用synchronized改進)

🌳4. 懶漢模式(使用雙重校驗鎖改進)

🥭三. volatile的原理?

🍏四. volatile的擴展問題(了解)


🍈一. 使用多線程需要考慮的因素

提高效率:使用多線程就是為了充分利用CPU資源,提高任務的效率
線程安全:使用多線程最基本的就是保障線程安全問題

所以我們在設計多線程代碼的時候就必須在滿足線程安全的前提下盡可能的提高任務執行的效
故:
加鎖細粒度化:加鎖的代碼少一點,讓其他代碼可以并發并行的執行

🍬考慮線程安全:

沒有操作共享變量的代碼沒有安全問題
對共享變量的使用volatile修飾變量即可
對共享變量的使用synchronized加鎖

🍊二. 單例模式

單例模式能保證某個類在程序中只存在唯一一份實例,而不會創建出多個實例
例如:DataSource(數據連接池),一個數據庫只需要一個連接池對象

單例模式分為餓漢模式懶漢模式

🌴1. 餓漢模式

餓漢模式是在類加載的時候就創建實例
這種方式是滿足線程安全的(JVM內部使用了加鎖,即多個線程調用靜態方法,只有一個線程競爭到鎖并且完成創建,只執行一次)

👁?🗨?實現代碼:

public class Singleton {//類加載階段就會創建實例private static Singleton instance = new Singleton();//為了防止不小心new了這個SingletonEH,所構造方法訪問權限為privateprivate Singleton(){}//提供一個方法可以讓外邊能夠拿到這個實例public static Singleton getInstance(){return instance;} }

🌾2. 懶漢模式

懶漢模式是在類加載的時候不創建實例,第一次使用的時候才創建

👁?🗨?實現代碼:

public class Singleton {private static Singleton instance = null;private Singleton(){}public static Singleton getInstance(){if(instance == null){instance = new Singleton();}return instance;} }

觀察上述代碼,在單線程下不存在線程安全問題,但是在多線程環境下存在安全問題嗎??

分析:
🍃當實例沒有被創建的時候,如果有多個線程都調用getInstance方法,就可能創建多個實例,就存在線程安全問題?
🍃但是實例一旦創建好,后面線程調用getInstance方法就不會出現線程安全問題

結果:線程安全問題出現在首次創建實例的時候

🌵3. 懶漢模式(使用synchronized改進)

我們使用sychronized修飾,👁?🗨?代碼如下:

public class Singleton {private static Singleton instance = null;private Singleton(){}public static synchronized Singleton getInstance(){if(instance == null){instance = new Singleton();}return instance;} }

這樣實現線程安全存在什么問題呢?

解析:
我們對方法使用synchronized修飾,也就是每次調用該方法的時候都會競爭鎖,但是創建實例只需要創建一次,也就是創建實例后,再調用該方法還需要競爭鎖釋放鎖

結果:雖然滿足線程安全,但是效率低

🌳4. 懶漢模式(使用雙重校驗鎖改進)

在上述代碼的基礎上進行改動:

使用雙重if判定,降低競爭鎖頻率
使用volatile修飾instance?

👁?🗨?實現代碼:

public class Singleton {//加volatile的原因:禁止指令重排序private static volatile Singleton instance = null;private Singleton(){}public static Singleton getInstance(){if(instance == null){ //外層的if判斷:如果實例被創建直接return,不讓線程再繼續競爭鎖//在沒有創建實例時,多個線程已經進入if判斷了//一個線程競爭到鎖,其他線程阻塞等待synchronized (Singleton.class) {//內層的if判斷,目的是讓競爭失敗的鎖如果再次競爭成功的話判斷實例是否被創建,創建釋放鎖return,沒有則創建if(instance == null){ instance = new Singleton();}}}return instance;} }

🍬對雙重if的解析:

🍂外層的if判斷:實例只是被創建一次,當實例已經被創建好了就不要后續操作,直接return返回
🍂內層的if判斷:實例未被創建時,多個線程同時競爭鎖,只有一個線程競爭成功并創建實例,其他競爭失敗的線程就會阻塞等待,當第一線程釋放鎖后,這些競爭失敗的線程就會繼續競爭,但是實例已經創建好了,所以需要再次進行if判斷?

畫圖分析,如下所示:

🥭三. volatile的原理?

volatile保證了可見性,有序性,在Java層面看,volatile是無鎖操作,多個線程對volatile修飾的變量進行讀可以并發并行執行,和無鎖執行效率差不多

volatile修飾的變量中,CPU使用了緩存一致性協議來保證讀取的都是最新的主存數據?

緩存一致性:如果有別的線程修改了volatile修飾的變量,就會把CPU緩存中的變量置為無效,要操作這個變量就要從主存中重新讀取

🍏四. volatile的擴展問題(了解)

🍬如果說volatile不保證有序性,雙重校驗鎖的寫法是否有問題?

關于new對象按順序分為3條指令:

🍁(1) 分配對象的內存空間
🍁(2) 實例化對象
🍁(3) 賦值給變量

正常的執行順序為(1)(2)(3),JVM可能會優化進行重排序后的順序為(1)(3)(2)

這個重排序的結果可能導致分配內存空間后,對象還沒有實例化完成,就完成了賦值
在這個錯誤的賦值后,instance==null不成立,線程就會拿著未完成實例化的instance,使用它的屬性和方法就會出錯

使用volatile保證有序性后:

線程在new對象時不管(1)(2)(3)是什么順序,后續線程拿到的instance是已經實例化完成
CPU里邊,基于volatile變量操作是有CPU級別的加鎖機制(它保證(1)(2)(3)全部執行完,寫回主存,再執行其他線程對該變量的操作)

總結

以上是生活随笔為你收集整理的单例模式中的线程安全问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。