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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

【多线程经典实例】实现一个线程安全的单例模式

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

文章目錄

    • 1.什么是單例模式
    • 2. 單例模式的組成
    • 3.餓漢模式實例
      • 3.1在餓漢模式中為什么在創建實例的時候使用static修飾?
      • 3.2 判斷該實例是否是線程安全的
    • 4.懶漢模式實例
      • 4.1 判斷該實例是否是線程安全的,如果不是線程安全的,那么怎樣修改可以成為線程安全的實例
    • 總結一下:

1.什么是單例模式

單例模式是設計模式中的一種,其實設計模式就好好比是一個棋譜,我們在日常下棋的時候會有一些經典的套路。那么在設計模式中也有這樣的經典套路。這些經典的套路都是有大佬前輩們實現的。我們在寫代碼的時候,有很多經典的場景,在經典場景中有一些經典的應對套路。大佬們把這些常見的應對手段給整理起來,就起來個名字–設計模式。有了設計模式,無論是新手程序員還是資深的老程序員,都會有一個代碼編程規范。以便讓初出茅廬的新手,代碼不至于寫的很糟糕。

2. 單例模式的組成

單例模式分為:餓漢模式懶漢模式。單例模式之所以被稱為單例模式,是因為我們在創建單例模式類的時候,就把該類的構造方法使用private進行修飾,以便在該類外,不能直接創建出一個實例。

3.餓漢模式實例

餓漢模式:指的是在單例模式中,在對單例進行初始化的時候,直接賦予單例實例,直接new出一個對象。

餓漢模式也可以這樣理解:我們平時在自己家里的時候,都洗過碗吧。就比如說,中午這頓飯使用了4個碗,在吃完飯后,我們立即就把4個碗給刷了。這里之所以被稱為餓漢模式是因為,在餓漢模式中,創建實例的時候比較著急。在初始化的時候,直接創建實例。

餓漢模式代碼案例:

//創建出一個單例模式//單例模式分為餓漢模式和懶漢模式 class Singleton{//在餓漢模式中,在進行初始化的時候,直接創建出實例public static Singleton instance = new Singleton();//使用private 修飾該類的構造方法,在類外無法創建出一個該類的對象private Singleton(){}//在類外調用該類中的實例public static Singleton getInstance(){return instance;} } public class TestDemo2 {public static void main(String[] args) {//Singleton singleton = new Singleton();Singleton single = Singleton.getInstance();} }

我們嘗試在main類中,自己創建出一個SingleTon的實例。

3.1在餓漢模式中為什么在創建實例的時候使用static修飾?

因為 static 修飾的成員更準確的說是類成員,類屬性、類方法,不加 static 修飾的成員準確的來說,就是實例方法,實例成員,實例屬性。

在一個java程序中,一個類方法只會存在一份(JVM保證的) 這也就是為什么要使用static對實例進行修飾的原因。進一步的就保證了在類的static 成員也只會存在一份。

在這里我們在深究一個static 關鍵字

其實在我們使用的編程語言java中,static表示的意思和這個單詞的字面意思完全不同,static 的意思大家知道 是靜態的。這其實是一個歷史遺留問題。

在C語言中的static有3個作用:

  • 修飾局部變量,把局部變量的生命周期變長。修飾一個全局變量,把這個全局變量的作用域限制到整個.c文件。

  • 修飾一個函數,把這個函數的作用域限制到整個.c文件。

    我們在這里也可以看出在c語言中static 關鍵字的英語本意和在c語言中的使用效果,也是對不上號的。

    其實在上古時期,那時候的static是表示把變量放到靜態內存區中,于是引入了static關鍵字,但是隨著計算機的發展,這個東西就逐漸的沒落了。但是static 關鍵字有被賦予了新的功能。

    在C++中 static關鍵字除了上述C語言的static 功能之外還有新的用法,修飾一個類的成員變量和成員函數,此處static 修飾的成員就表示為類成員。

    Java語言就是把C++中static 的功能繼承過來了而已。

既然static 關鍵字的本意和它的對應效果對不上號,那么為什么不使用其他的詞呢?

在一個編程語言中,要想新增一個關鍵字,是一件非常有風險的事情。因為不能還在程序中的單詞重合。

SingleTon.class 類對象,就是.class文件被JVM加載到內存中,表現出來的模樣。類對象就有著.class文件的所有信息。就像類名,屬性等都可以有SingleTon.class中找到。這樣也就實現了反射

3.2 判斷該實例是否是線程安全的

餓漢模式是線程安全的

那么為什么餓漢模式樣式的單例模式是線程安全的呢?我們在程序的哪里判斷該單例模式是線程安全的?

線程安不安全,具體是在多線程環境之下,并發調用的getInstance()方法是否會產生bug?

在博主的上一篇文章中,介紹了產生線程不安全的案例。

造成線程不安全的案例有5種。

  • 線程搶占式執行,線程間的調度充滿了隨機性。
  • 多線程對一個變量進行修改。
  • 針對變量操作不是原子的
  • 內存可見性問題
  • 指令重排序問題。
  • 我們現在回顧在餓漢模式中的getInstance()方法,在該方法中只有一個return操作,就是對一個變量進行了讀取,符合針對變量操作的原子性。所以是線程安全的

    4.懶漢模式實例

    懶漢模式創建懶漢模式的單例模式的時候,我們不著急創建出實例。

    還是那洗碗舉例,我們中午吃飯的時候,使用了4個碗,吃完飯后,我們不著急洗碗,到了晚上吃飯的時候,需要使用的幾個碗,那么我們現在就洗幾個碗。假如說我們晚上要使用2個碗,那么就洗兩個,剩下的兩個碗不管。

    在我們平時生活中,餓漢模式比懶漢模式好,因為你試一試中午吃完飯不洗碗,如果不洗碗肯定會被挨罵。但是在我們的計算機中可未必,懶漢模式要比餓漢模式要好一些。

    那么為什么在計算機中懶漢模式要比餓漢模式要好一些呢?

    就比如說,現在有一個1G的圖片文件,如果按照餓漢模式,那么計算機就會在內存中一下把這1G大的圖片文件全部加載出來,這不就耗費CUP資源,并且如果計算機用戶在瀏覽圖片文件的時候,就看了一點沒有全部把圖片文件瀏覽完。那么這樣的餓漢模式不就是費力不討好好嗎?

    反而看看我們的懶漢模式,之所以懶,是因為你趕它一些它走一下。在懶漢模式中,我們一次不會再內存中把所有的圖片加載完,而是把計算機用戶的一個計算機屏幕中的圖片加載出來。用戶滑動一下滾動條,加載一下。這樣就進行了優化。

    其實在計算機中懶漢模式是褒義詞,但是在現實世界中就算了吧😂

    class Singleton2{//懶漢模式,在該模式中不著急創建出實例,在類外需要的時候,我們再進行創建public static Singleton2 instance = null;private Singleton2(){}public static Singleton2 getInstance(){if(instance == null){instance = new Singleton2();}return instance;} } public class TestDemo3 {public static void main(String[] args) {Singleton2 singleton2 = Singleton2.getInstance();} }

    4.1 判斷該實例是否是線程安全的,如果不是線程安全的,那么怎樣修改可以成為線程安全的實例

    首先上述的所謂的懶漢模式的單例模式不是線程安全的

    那么為什么它不是線程安全的呢?

    因為在多線程中,我們調用懶漢模式中的getInstance()方法的時候,針對變量的操作不是原子的,那么有從哪可以看出不是原子的呢?

    如圖:

    那么針對變量操作不是原子性的,它的解決辦法就是進行加鎖,使用synchronized關鍵字進行加鎖!!!

    修改之后的代碼:

    //實現一個線程安全的單例模 class Singleton2{//懶漢模式,在該模式中不著急創建出實例,在類外需要的時候,我們再進行創建public static Singleton2 instance = null;private Singleton2(){}public static Singleton2 getInstance(){synchronized(Singleton2.class) {if (instance == null) {instance = new Singleton2();}}return instance;} } public class TestDemo3 {public static void main(String[] args) {Singleton2 singleton2 = Singleton2.getInstance();} }

    我們知道如果遇到可針對變量操作不是原子的,要使用synchronized關鍵字進行加鎖,但是也不是說,代碼中有了synchroniezd關鍵字就一定不會線程安全,我們要把synchronized關鍵字加對地方。synchronized加的位置正確,不能隨便寫。

    //類對象在一個類中只有唯一一份,就能保證調用的getInstance的時候都是針對都一個對象進行加鎖 synchronized(SingleTon2.class){}

    但是我們加鎖之后,又帶來的新的問題!!!

    對于剛才的這個懶漢模式的代碼而言,線程不安全發生在instance沒有被初始化之前,未被初始化的時候,多線程調用getInstance()方法時,會存在線程安全問題,因為涉及到讀和修改。但是在instance初始化之后,instance一定不是null,if條件一定不成立,getInstance()就只剩下兩個讀操作,也就是說instance初始化之后,線程就是安全的了。

    并且按照上述的加鎖操作,無論是代碼中的instance初始化之前,還是初始化之后。每次調用getInstance()方法的時候,都會對其進行加鎖。也就意味著即使初始化之后(已經線程安全了),但是仍然存在大量的鎖競爭。

    既然這里的instance已經被初始化過了,即使這里的條件在不能被滿足了,但是仍然會調用getInstance()方法,都需要進行加鎖,也就可能會產生鎖競爭,但是我們知道這里的鎖競爭其實是沒有必要的。

    我們知道加鎖確實能讓線程安全,但是同時也付出了代價,一旦在一個線程中加了鎖之后,那么就和運行高效無關了。(程序的速度就變慢了)因為加鎖之后,線程之間是串行執行的。代碼的運行效率就變慢了。

    博主以前說過開發效率要比運行效率更重要,一切都要從程序員的利益出發,但是運行效率也不是說不重要!如果說運行效率不重要的話,那么我們在前面學習那么多的數據結構干啥,不都是使用一個較好的數據結構,來組織數據,讓代碼變得有效嘛

    改進方案:

    在instance初始化之前,才進行加鎖,在初始化之后,不進行加鎖。在加鎖這里在加一個條件判斷即可

    代碼如下:

    //實現一個線程安全的單例模 class Singleton2{//懶漢模式,在該模式中不著急創建出實例,在類外需要的時候,我們再進行創建public static Singleton2 instance = null;private Singleton2(){}public static Singleton2 getInstance(){if(instance == null) { //如果instance被初始化過了,那么就不必再進行加鎖,直接返回這個實例即可synchronized (Singleton2.class) {if (instance == null) {instance = new Singleton2();}}}return instance;} } public class TestDemo3 {public static void main(String[] args) {Singleton2 singleton2 = Singleton2.getInstance();} }

    我們在上述的代碼中,可以看到在getInstance()方法中,使用了兩個條件判斷語句,都是判斷instance==null ,但是這兩個條件判斷語句的實際含義是千差萬別。這兩個添加判斷長得一樣,純屬是一個美麗的錯誤。

    上面的條件判斷是是否需要加鎖。也就是說現在的instance是否已經被初始化過了

    下面的條件判斷是是否需要創建實例。

    我們如果去掉了里層的條件判斷語句那么就會變成:

    //實現一個線程安全的單例模 class Singleton2{//懶漢模式,在該模式中不著急創建出實例,在類外需要的時候,我們再進行創建public static Singleton2 instance = null;private Singleton2(){}public static Singleton2 getInstance(){if(instance == null) {synchronized (Singleton2.class) { //其實在這里加鎖,就是加了個寂寞,在這里只針對設置實例加鎖,在加鎖語句的外面,還有istance == null 涉及到 讀和判斷,所以說加鎖和沒加一樣,還是不符合原子性。instance = new Singleton2();}}return instance;} } public class TestDemo3 {public static void main(String[] args) {Singleton2 singleton2 = Singleton2.getInstance();} }

    如果直接對getInstance()方法進行加鎖,那么就是一個無腦加鎖

    此處博主告訴各位老鐵,在上述的代碼中,還存在一個問題。但是已經線程安全了呀,哪里還有錯呢?我們在單例模式中使用volatile,主要是使用volatile可以進制指令重排序,從而保證程序的正常運行。

    public class Singleton {private Singleton() {}// 使用 volatile 禁止指令重排序private static volatile Singleton instance = null;public static Singleton getInstance() {if (instance == null) { // 1synchronized (Singleton.class) {if (instance == null) {instance = new Singleton(); // 2}}}return instance;} }

    注意觀察上述代碼,我標記了第 ① 處和第 ② 處的兩行代碼。給私有變量加 volatile 主要是為了防止第 ② 處執行時,也就是“instance = new Singleton()”執行時的指令重排序的,這行代碼看似只是一個創建對象的過程,然而它的實際執行卻分為以下 3 步:

    創建內存空間。
    在內存空間中初始化對象 Singleton。
    將內存地址賦值給 instance 對象(執行了此步驟,instance 就不等于 null 了)。

    試想一下,如果不加 volatile,那么線程 1 在執行到上述代碼的第 ② 處時就可能會執行指令重排序,將原本是 1、2、3 的執行順序,重排為 1、3、2。但是特殊情況下,線程 1 在執行完第 3 步之后,如果來了線程 2 執行到上述代碼的第 ① 處,判斷 instance 對象已經不為 null,但此時線程 1 還未將對象實例化完,那么線程 2 將會得到一個被實例化“一半”的對象,從而導致程序執行出錯,這就是為什么要給私有變量添加 volatile 的原因了。

    總結一下:

    實現一個線程安全的單例模式—針對懶漢模式

  • 在正確的位置加鎖
  • 雙重if判定
  • volatile關鍵字
  • 總結

    以上是生活随笔為你收集整理的【多线程经典实例】实现一个线程安全的单例模式的全部內容,希望文章能夠幫你解決所遇到的問題。

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

    主站蜘蛛池模板: 亚洲伦理中文字幕 | 一区二区三区黄色 | 日本不卡视频一区二区 | 日本啪啪网站 | 国产成人一区二区三区电影 | 欧美三级韩国三级日本三斤在线观看 | 天天爽夜夜爽一区二区三区 | 国产一区二区电影 | 另类小说婷婷 | 国产91精品看黄网站在线观看 | 男人的天堂视频网站 | 国产www性| 中文天堂在线视频 | 亚洲精品乱码久久久久久久 | 97干干干 | 自拍偷拍校园春色 | 日韩精品理论 | 三级在线观看网站 | 成人免费自拍视频 | 欧美日韩中文字幕一区二区三区 | 色综合久久88色综合天天免费 | 欧美专区第一页 | 丁香六月在线 | 国产色自拍 | 麻豆视频一区二区 | 国产免费av片在线观看 | 神马伦理影视 | 亚洲综合激情 | 中文字幕第2页 | 国产丝袜视频在线 | 久久久福利视频 | 大胸奶汁乳流奶水出来h | 91香蕉视频在线 | 亚洲伦理在线视频 | 苍井空浴缸大战猛男120分钟 | 久久免费视频播放 | 中文字幕在线第一页 | 亚洲精品大片www | 草草影院在线 | 亚洲av综合色区无码二区爱av | 国产欧美精品区一区二区三区 | 国产乱人乱偷精品视频 | 国产99视频在线 | 成人一区二区三区视频 | 国内毛片视频 | 老熟女高潮一区二区三区 | 穿情趣内衣被c到高潮视频 欧美性猛交xxxx黑人猛交 | 黑人100部av解禁片 | 警察高h荡肉呻吟男男 | xxxxx色| 欧美激情免费在线观看 | 国产xx在线观看 | 9久久9毛片又大又硬又粗 | 少妇人妻好深好紧精品无码 | 日韩精品一区三区 | av噜噜在线观看 | h片在线播放 | www.国产区 | 可以免费看的av网站 | 亚洲在线一区二区 | 日韩伦理中文字幕 | 男人都懂的网址 | 全程偷拍露脸中年夫妇 | 欧美国产一二三区 | 想要视频在线 | 免费av成人| 欧美综合色 | 韩国一级淫片免费看 | 久久精品国产视频 | 国产传媒在线观看 | 久久色图 | 911成人网| 强行糟蹋人妻hd中文字幕 | 久久久综合久久久 | 亚洲天堂一级片 | 国语对白av | 中文字幕电影一区 | 亚洲视频免费观看 | 天天精品 | 极品少妇一区二区三区 | 婷婷去俺也去 | 总裁边开会边做小娇妻h | www.久久久久久久 | 国产-第1页-浮力影院 | 免费在线观看黄网站 | 性色av蜜臀av浪潮av老女人 | 黄色在线观看网址 | 久久久久国产精 | 国产成人无码一区二区在线播放 | 国产精品一品二区三区的使用体验 | 99在线观看 | 51精品 | 欧美一区二区三区婷婷 | 日韩黄色av网站 | 欧美一区二区三区啪啪 | 日本不卡一区二区在线观看 | 黄色大片子| 国产精品国产精品 | 各处沟厕大尺度偷拍女厕嘘嘘 |