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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

多线程--单例模式

發(fā)布時(shí)間:2023/12/10 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 多线程--单例模式 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

單例模式是23種設(shè)計(jì)模式中比較簡(jiǎn)單的模式之一,本博客較為詳細(xì)的梳理了該設(shè)計(jì)模式,并實(shí)現(xiàn)該模式。

問題由來 : 我們?yōu)槭裁葱枰獑卫J?

在許多時(shí)候,一個(gè)系統(tǒng)只需要一個(gè)全局對(duì)象,這樣有利于我們協(xié)調(diào)系統(tǒng)的整體行為。比如在某個(gè)服務(wù)器程序中,該服務(wù)器的配置信息存放在一個(gè)文件中,這些配置數(shù)據(jù)由一個(gè)單例對(duì)象統(tǒng)一讀取,然后服務(wù)進(jìn)程中的其他對(duì)象再通過這個(gè)單例對(duì)象獲取這些配置信息。這樣做可以簡(jiǎn)化對(duì)系統(tǒng)的管理,并且可以避免出現(xiàn)不一致的狀況。

如何解決 : 單例模式的概念

在Java中,單例模式就是確保一個(gè)類中只有一個(gè)實(shí)例,并且它可以提供一個(gè)全局的訪問點(diǎn)以訪問該實(shí)例。它可以為整個(gè)系統(tǒng)提供一個(gè)全局訪問點(diǎn),避免了(不必要的)頻繁的創(chuàng)建與銷毀對(duì)象,可以提高系統(tǒng)性能,節(jié)省資源,并且便于整體把控系統(tǒng)。

概念模型 : 在java中建立單例模式的模型

在Java中,萬物皆對(duì)象,那么,如何將單例模式的概念抽象為Java中的對(duì)象呢?

將單例模式看作一類對(duì)象,則這類對(duì)象需要有三大要素:

  • 指向自己(唯一)實(shí)例的私有靜態(tài)引用 :因?yàn)橹挥幸粋€(gè)唯一的實(shí)例,所以用靜態(tài)引用
  • 私有的構(gòu)造方法 : 避免外部創(chuàng)建實(shí)例對(duì)象
  • 公有的以自己(唯一)實(shí)例為返回值的靜態(tài)方法:提供一個(gè)訪問該實(shí)例的方法

類的實(shí)現(xiàn) :

單例模式的實(shí)現(xiàn)有兩種比較經(jīng)典的實(shí)現(xiàn)方法

  • 餓漢模式(立即加載模式):在加載該類的時(shí)候就實(shí)例化對(duì)象
  • 懶漢模式(延遲加載模式):在真正使用的時(shí)候才實(shí)例化對(duì)象
  • 單線程實(shí)現(xiàn) :

    餓漢模式 :

    class Hunger_Thread{// 指向自己實(shí)例的私有靜態(tài)引用,// 在類加載時(shí)便new了一個(gè)對(duì)象(類只會(huì)加載一次,所以只會(huì)new一個(gè)對(duì)象)private static Hunger_Thread hunger = new Hunger_Thread();// 私有構(gòu)造方法private Hunger_Thread(){};// 以自己實(shí)例為返回值的靜態(tài)公有方法public static Hunger_Thread getHunger(){return hunger;}}

    懶漢模式 :

    class Lazy_Thread{// 指向自己實(shí)例的私有靜態(tài)引用private static Lazy_Thread lazy;// 私有構(gòu)造方法private Lazy_Thread(){}// 以自己實(shí)例為返回的靜態(tài)公有方法public static Lazy_Thread getLazy(){if(lazy==null){lazy = new Lazy_Thread();}return lazy;} }

    多線程實(shí)現(xiàn) :

    多線程需要考慮線程安全問題 ,由于餓漢模式(不管是單線程還是多線程)只會(huì)在類加載時(shí)new對(duì)象,由于一個(gè)類在整個(gè)生命周期中只會(huì)被加載一次,因此該單例類只會(huì)創(chuàng)建一個(gè)實(shí)例,故它是線程安全的。

    但懶漢模式會(huì)出現(xiàn)線程不安全的例子 :(就拿單線程的懶漢代碼為例)

    假設(shè)有線程 t1,t2,這兩個(gè)線程都調(diào)用了getLazy()方法(lazy==null,t1先執(zhí)行),t1執(zhí)行到 if(lazy==null){...}(剛判斷完,還未創(chuàng)建實(shí)例)時(shí),時(shí)間片用完了,此時(shí)該t2執(zhí)行,t2完整的執(zhí)行了該方法(已經(jīng)創(chuàng)建了實(shí)例),又該t1執(zhí)行了,此時(shí)它并不知道已經(jīng)創(chuàng)建了實(shí)例,所以還會(huì)創(chuàng)建一個(gè),這就與該模式的設(shè)計(jì)理念不符,也就是線程不安全。

    解決方法也很簡(jiǎn)單,加鎖就可以了

    class Lazy_Thread{// 指向自己實(shí)例的私有靜態(tài)引用private static Lazy_Thread lazy;// 私有構(gòu)造方法private Lazy_Thread(){}// 以自己實(shí)例為返回的靜態(tài)公有方法public static synchronized Lazy_Thread getLazy(){if(lazy==null){lazy = new Lazy_Thread();}return lazy;} }

    這樣可以解決線程安全問題,但是效率低下,鎖的粒度太大。下面的代碼段與上面的相比沒什么效率的提升。

    class Lazy_Thread{// 指向自己實(shí)例的私有靜態(tài)引用private static Lazy_Thread lazy;// 私有構(gòu)造方法private Lazy_Thread(){}// 以自己實(shí)例為返回的靜態(tài)公有方法public static Lazy_Thread getLazy(){synchronized (Lazy_Thread.class){if(lazy==null){lazy = new Lazy_Thread();}}return lazy;} }

    一般,我們會(huì)使用雙重檢查來實(shí)現(xiàn)線程安全,這是一種比較高效的做法。

    class Lazy_Thread{// 要用 volatile 修飾,防止指令重排序private static volatile Lazy_Thread lazy;private Lazy_Thread(){}// 雙重檢查實(shí)現(xiàn)線程安全public static Lazy_Thread getLazy(){if(lazy==null){// 只有第一次創(chuàng)建實(shí)例時(shí)才加鎖synchronized (Lazy_Thread.class){if(lazy==null){lazy = new Lazy_Thread();}}}return lazy;} }

    為什么要用 volatile 修飾 lazy ?

    ?new AnyClass();是一個(gè)非原子操作 ,大致會(huì)分為這三步

  • 分配對(duì)象的內(nèi)存空間
  • 初始化對(duì)象
  • 將該對(duì)象的引用指向(第一步)分配好的內(nèi)存空間
  • 如果發(fā)生了指令重排序,則有可能把步驟3放在步驟2之前執(zhí)行(lazy不為null,但是它沒有初始化,它就是一個(gè)殘缺的對(duì)象)。

    假設(shè)線程t1在執(zhí)行new的時(shí)候發(fā)生了指令重排序,new的步驟變成了1,3,2,而t1在執(zhí)行完3后時(shí)間片剛好用完(此時(shí) lazy 就是一個(gè)殘缺的對(duì)象)這時(shí),t2進(jìn)入線程,判斷 lazy != null 之間返回的殘缺的對(duì)象,這也是線程不安全的!

    總結(jié)

    以上是生活随笔為你收集整理的多线程--单例模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

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