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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java多线程之单例模式在多线程环境下的安全问题

發布時間:2024/2/28 java 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java多线程之单例模式在多线程环境下的安全问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Java多線程之單例模式在多線程環境下的安全問題


目錄:

  • 單例模式基本概念
  • 單線程下的單例模式
  • 多線程下的單例模式
  • 單例模式volatile分析

  • 1. 單例模式基本概念

    基本概念轉載自:單例模式|菜鳥教程

    單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。

    這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。

    注意:

  • 單例類只能有一個實例。
  • 單例類必須自己創建自己的唯一實例。
  • 單例類必須給所有其他對象提供這一實例。
  • 意圖:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。

    主要解決:一個全局使用的類頻繁地創建與銷毀。

    何時使用:當您想控制實例數目,節省系統資源的時候。

    如何解決:判斷系統是否已經有這個單例,如果有則返回,如果沒有則創建。

    關鍵代碼:構造函數是私有的。

    應用實例:

  • 一個班級只有一個班主任。
  • Windows 是多進程多線程的,在操作一個文件的時候,就不可避免地出現多個進程或線程同時操作一個文件的現象,所以所有文件的處理必須通過唯一的實例來進行。
  • 一些設備管理器常常設計為單例模式,比如一個電腦有兩臺打印機,在輸出的時候就要處理不能兩臺打印機打印同一個文件。
  • 優點:

  • 在內存里只有一個實例,減少了內存的開銷,尤其是頻繁的創建和銷毀實例(比如管理學院首頁頁面緩存)。
  • 避免對資源的多重占用(比如寫文件操作)。
    缺點:沒有接口,不能繼承,與單一職責原則沖突,一個類應該只關心內部邏輯,而不關心外面怎么樣來實例化。
  • 使用場景:

  • 要求生產唯一序列號。
  • WEB 中的計數器,不用每次刷新都在數據庫里加一次,用單例先緩存起來。
  • 創建的一個對象需要消耗的資源過多,比如 I/O 與數據庫的連接等。
  • 注意事項:getInstance() 方法中需要使用同步鎖 synchronized (Singleton.class) 防止多線程同時進入造成 instance 被多次實例化。


    2. 單線程下的單例模式


    1. 單線程下單例模式代碼

    public class SingletonDemo {private static SingletonDemo instance = null;private SingletonDemo(){System.out.println(Thread.currentThread().getName()+"\t 我是構造方法SingletonDemo");}public static SingletonDemo getInstance(){if (instance == null){instance = new SingletonDemo();}return instance;}public static void main(String[] args) {// 單線程(main線程的操作動作)System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());} }

    2. 編譯結果


    3. 多線程下的單例模式


  • 多線程下,上面的代碼執行結果不再是單例,結果如下(不固定)

  • 2.解決辦法,可以在getInstance()方法上加synchronized,但是不推薦。更好的解決辦法是使用DCL(Double Check Lock 雙端撿鎖機制)

    代碼如下:

    public class SingletonDemo {private static SingletonDemo instance = null;private SingletonDemo() {System.out.println(Thread.currentThread().getName() + "\t 我是構造方法SingletonDemo");}//DCL (Double Check Lock 雙端撿鎖機制)public static SingletonDemo getInstance() {if (instance == null) {synchronized (SingletonDemo.class) {if (instance == null) {instance = new SingletonDemo();}}}return instance;}public static void main(String[] args) {//并發多線程后,情況發生了很大的變化for (int i = 1; i <= 20; i++) {new Thread(() -> {SingletonDemo.getInstance();}, String.valueOf(i)).start();}} }

    4. 單例模式volatile分析


  • 上面多線程下單例模式在99.9%情況下都正確,但還是不能保證完全正確。因為在多線程環境下,底層為了優化有指令重排。
  • 解決辦法:加入volatile。
  • 代碼如下

    public class SingletonDemo {private static volatile SingletonDemo instance = null;private SingletonDemo() {System.out.println(Thread.currentThread().getName() + "\t 我是構造方法SingletonDemo");}//DCL (Double Check Lock 雙端撿鎖機制)public static SingletonDemo getInstance() {if (instance == null) {synchronized (SingletonDemo.class) {if (instance == null) {instance = new SingletonDemo();}}}return instance;}public static void main(String[] args) {//并發多線程后,情況發生了很大的變化for (int i = 1; i <= 20; i++) {new Thread(() -> {SingletonDemo.getInstance();}, String.valueOf(i)).start();}} }
  • 具體分析:

    DCL(雙端檢鎖)機制不一定線程安全,原因是有指令重排序的存在,加入volatile可以禁止指令重排。

    原因在于某一個線程執行到第一次檢測,讀取到的instance不為null時,instance的引用對象可能沒有完成初始化。

    instance=newSingDemo();可以分為以下3步完成(偽代碼)

    memory=allocate(): // 1.分配對象內存空間
    instance(memory): // 2.初始化對象
    instance=memory; //3. 設置instance指向剛分配的內存地址,此時instance != null

    步驟2和步驟3不存在數據依賴關系,而且無論重排前還是重排后程序的執行結果在單線程中并沒有改變,因此這種重排優化是允許的。

    memory=allocate();//1.分配對象內存空間
    instance=memory;//3.設置ins怡nce指向剛分配的內存地址,此時instance != null,但是對象還沒有初始化完成!
    instance(memory);//2.初始化對象

    但是指令重排只會保證串行語義執行的一致性(單線程),但并不會關心多線程間的語義一致性。

    所以當一個線程訪問instance不為null時,由于instance實例未必已初始化完成,也就造成了線程安全問題


  • 總結

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

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