Java單例的常見形式

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

Java單例的常見形式

本文目的:總結Java中的單例模式

本文定位:學習筆記

學習過程記錄,加深理解,便于回顧。也希望能給學習的同學一些靈感

一、非延遲加載單例類

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

二、同步延遲加載

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

三、雙重檢測同步延遲加載

為了減少同步的開銷,于是有了雙重檢查模式

為處理原版非延遲加載方式瓶頸問題,我們需要對 instance 進行第二次檢查,目的是避開過多的同步(因為這里的同步只需在第一次創建實例時才同步,一旦創建成功,以后獲取實例時就不需要同獲取鎖了),但在Java中行不通,因為同步塊外面的if (instance == null)可能看到已存在,但不完整的實例。

public class Singleton {private volatile static Singleton instance = null;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized(Singleton.class) { // 1  if (instance == null) { // 2  instance = new Singleton(); // 3  }}}return instance;}
}

雙重檢測鎖定失敗的問題并不歸咎于 JVM 中的實現 bug,而是歸咎于 Java 平臺內存模型。內存模型允許所謂的“無序寫入”,這也是失敗的一個主要原因。

無序寫入: 為解釋該問題,需要重新考察上述清單中的 //3 行。此行代碼創建了一個 Singleton 對象并初始化變量 instance 來引用此對象。這行代碼的問題是:在 Singleton 構造函數體執行之前,變量 instance 可能成為非 null 的,即賦值語句在對象實例化之前調用,此時別的線程得到的是一個還會初始化的對象,這樣會導致系統崩潰。 什么?這一說法可能讓您始料未及,但事實確實如此。在解釋這個現象如何發生前,請先暫時接受這一事實,我們先來考察一下雙重檢查鎖定是如何被破壞的。假設代碼執行以下事件序列:

  1. 線程 1 進入 getInstance() 方法。
  2. 由于 instance 為 null,線程 1 在 //1 處進入 synchronized 塊。
  3. 線程 1 前進到 //3 處,但在構造函數執行之前,使實例成為非 null。
  4. 線程 1 被線程 2 預占。
  5. 線程 2 檢查實例是否為 null。因為實例不為 null,線程 2 將 instance 引用返回給一個構造完整但部分初始化了的 Singleton 對象。
  6. 線程 2 被線程 1 預占。
  7. 線程 1 通過運行 Singleton 對象的構造函數并將引用返回給它,來完成對該對象的初始化。

為展示此事件的發生情況,假設代碼行 instance =new Singleton(); 執行了下列偽代碼:

mem = allocate();             //為單例對象分配內存空間.
instance = mem;               //注意,instance 引用現在是非空,但還未初始化
ctorSingleton(instance);    //為單例對象通過instance調用構造函數

這段偽代碼不僅是可能的,而且是一些 JIT 編譯器上真實發生的。執行的順序是顛倒的,但鑒于當前的內存模型,這也是允許發生的。JIT 編譯器的這一行為使雙重檢查鎖定的問題只不過是一次學術實踐而已。

在 Java 中雙重檢查模式無效的原因是在不同步的情況下引用類型不是線程安全的。對于除了 long 和 double 的基本類型,雙重檢查模式是適用 的。比如下面這段代碼就是正確的:

private int count;  
public int getCount(){  if (count == 0){   synchronized(this){   if (count == 0){  count = computeCount();  //一個耗時的計算  }     }    }  return count;  
} 

上面就是關于java中雙重檢查模式(double-check idiom)的一般結論。

double-check無效原因可參見

但是事情還沒有結束,因為java的內存模式也在改進中。Doug Lea 在他的文章中寫道:“根據最新的 JSR133 的 Java 內存模型,如果將引用類型聲明為 volatile,雙重檢查模式就可以工作了”, 參見 。

Section 2.2.7.x on the Memory Model doesn't provide current details of the JSR133 spec revision. (The basic ideas still apply though.) One change that commonly arises in practice is that Section 2.2.7.4 and 2.4.1.2 should say that reading a volatile reference makes visible other changes to the referred object by the thread writing the reference. In particular, double-check idioms work in the expected way when references are declared volatile.

所以以后要在 Java 中使用雙重檢查模式,可以使用下面的代碼:

private volatile Resource resource;  
public Resource getResource(){  if (resource == null){   synchronized(this){   if (resource==null){  resource = new Resource();    }     }    }  return resource;  
}  

四、使用內部類實現延遲加載(推薦)

推薦方法Initialization-on-demand holder idiom

public class Singleton {  static class SingletonHolder {  static Singleton instance = new Singleton();  }  public static Singleton getInstance(){  return SingletonHolder.instance;  }  
} 

其他

以上為常見單例形式,另有 ThreadLocal、枚舉 等實現形式

此處不作介紹

參考

  • http://www.cnblogs.com/dolphin0520/p/3920373.html#!comments
  • http://www.iteye.com/topic/652440
  • http://www.iteye.com/topic/260515
  • https://blog.csdn.net/dl88250/article/details/5439024
  • https://blog.csdn.net/chenchaofuck1/article/details/51702129

對本文有什么建議(內容、寫作風格等),歡迎留言提出,感謝!

轉載于:https://my.oschina.net/lichuangnk/blog/1856430

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/254095.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/254095.shtml
英文地址,請注明出處:http://en.pswp.cn/news/254095.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

運動控制卡的基類函數與實現例子

基類 namespace MotionCardDll {public abstract class IMotionCard{public Int32 m_Mode;public Int32 m_BoardId;//Card 號public Int32 m_Card_name;public Int32 m_StartAxisID

U-Boot啟動過程完全分析

1.1 U-Boot 工作過程 U-Boot啟動內核的過程可以分為兩個階段,兩個階段的功能如下: (1)第一階段的功能 硬件設備初始化 加載U-Boot第二階段代碼到RAM空間 設置好棧 跳轉到第二階段代碼入口 (2&#x…

CJOJ 2171 火車站開飯店(樹型動態規劃)

CJOJ 2171 火車站開飯店(樹型動態規劃) Description 政府邀請了你在火車站開飯店,但不允許同時在兩個相連的火車站開。任意兩個火車站有且只有一條路徑,每個火車站最多有 50 個和它相連接的火車站。 告訴你每個火車站的利潤&#…

JavaWeb總結(十五)

AJAX(Asynchronous JavaScript and XML(異步的 JavaScript 和 XML)) AJAX的作用是什么? 在無需重新加載整個網頁的情況下,能夠更新部分網頁的技術 是一種用于創建快速動態網頁的技術 通過在后臺與服務器進行…

工業相機基類與實現

基類 namespace Cameron {//相機參數public struct CamPara{public int DeviceID; //設備描述public string Name;public int WorkMode; //工作類型,0為連續模式,1為觸發模式public float Expours

物聯網技術周報第 143 期: Unity 3D 和 Arduino 打造虛擬現實飛行器

新聞 \\\\t《西門子、阿里云簽約助力中國工業物聯網發展》德國工業集團西門子和中國阿里巴巴集團旗下的云計算公司阿里云9日在柏林簽署備忘錄,共同推進中國工業物聯網發展。根據備忘錄內容,西門子和阿里云將發揮各自技術和行業優勢&#xff…

不同平臺下 sleep區別用法

應用程序&#xff1a; #include <syswait.h> usleep(n) //n微秒 Sleep&#xff08;n&#xff09;//n毫秒 sleep&#xff08;n&#xff09;//n秒 驅動程序&#xff1a; #include <linux/delay.h> mdelay(n) //微秒milliseconds 其實現 #ifdef notdef #define mdelay…

各視頻、各音頻之間格式任意玩弄(圖文詳解)

寫在前面說的話 在這里&#xff0c;記錄下來&#xff0c;是為了方便以后偶爾所制作所需和你們前來的瀏覽學習。 學會&#xff0c;玩弄一些視頻和音頻的軟件&#xff0c;只有好處沒有害處。同時&#xff0c;也不需很多時間&#xff0c;練練手罷了。也是方便自己所用吧&#xff0…

oracle 如何查看日志?

2019獨角獸企業重金招聘Python工程師標準>>> Oracle日志查看一&#xff0e;Oracle日志的路徑&#xff1a;登錄&#xff1a;sqlplus "/as sysdba"查看路徑&#xff1a;SQL> select * from v$logfile;SQL> select * from v$logfile;(#日志文件路徑)二…

回歸_英國酒精和香煙關系

sklearn實戰-乳腺癌細胞數據挖掘(博客主親自錄制視頻教程) https://study.163.com/course/introduction.htm?courseId1005269003&utm_campaigncommission&utm_sourcecp-400000000398149&utm_mediumshare 數據統計分析聯系:&#xff31;&#xff31;&#xff1a;&a…

C# ini文件讀寫函數

namespace Tools {class IniOperate{[DllImport("kernel32")]private static extern int GetPrivateProfileString(string section, string key,

Visual studio內存泄露檢查工具--BoundsChecker

BoundsChecker是一個Run-Time錯誤檢測工具&#xff0c;它主要定位程序在運行時期發生的各種錯誤。 BoundsChecker能檢測的錯誤包括&#xff1a; 1&#xff09;指針操作和內存、資源泄露錯誤&#xff0c;比如&#xff1a;內存泄露&#xff1b;資源泄露&#xff…

【轉】如何用Maven創建web項目(具體步驟)

使用eclipse插件創建一個web project 首先創建一個Maven的Project如下圖 我們勾選上Create a simple project &#xff08;不使用骨架&#xff09; 這里的Packing 選擇 war的形式 由于packing是war包&#xff0c;那么下面也就多出了webapp的目錄 由于我們的項目要使用eclipse發…

CST光源控制卡簡單操作C#程序

namespace Machine {class LightCST{private SerialPort serialPort ;public LightCST(){serialPort = new SerialPort();}

可能是目前最詳細的Redis內存模型及應用解讀

Redis是目前最火爆的內存數據庫之一&#xff0c;通過在內存中讀寫數據&#xff0c;大大提高了讀寫速度&#xff0c;可以說Redis是實現網站高并發不可或缺的一部分。 我們使用Redis時&#xff0c;會接觸Redis的5種對象類型&#xff1a;字符串、哈希、列表、集合、有序集合。豐富…

bootcmd 和bootargs

看到這個標題&#xff0c;可能覺得這個并沒有什么的&#xff0c;其實不然&#xff0c;編好了u-boot了&#xff0c;但是如何來使用確不是那么簡單的&#xff0c;想當初我將uboot制作出來后以為全部都搞定了&#xff0c;屁顛屁顛的燒到板子上后可系統就是起不來&#xff0c;為什么…

名詞解釋(容器、并發,插件,腳本)及程序對象的創建和注釋文檔

一、專有名詞 1‘  容器 創建一種對象類型&#xff0c;持有對其他對象的引用&#xff0c;被稱為容器的新對象。在任何時候都可以擴充自己以容納置于其中的所有東西。 java在其標準類庫中包含了大量的容器。在某些類庫中&#xff0c;一兩個通用容器足以滿足所有的需要&#xf…

POJ 1696 Space Ant 極角排序(叉積的應用)

題目大意&#xff1a;給出n個點的編號和坐標&#xff0c;按逆時針方向連接著n個點&#xff0c;按連接的先后順序輸出每個點的編號。 題目思路&#xff1a;Cross&#xff08;a,b&#xff09;表示a,b的叉積&#xff0c;若小于0&#xff1a;a在b的逆時針方向&#xff0c;若大于0a在…

C#模板匹配創建模板與查找模板函數

class ShapeModulInspect{/// <summary>/// /// </summary>/// <param name="InspectImg">圖像</param>/// <param name="ModulRoi">ROI</param>/// <param name="AngleStart">起始角</param>/…

SuperMap iDesktop之導入數據

SuperMap作為一個平臺軟件有自己的數據格式&#xff0c;現要將ESRI的SHP數據導入到SuperMap的udb數據庫中&#xff0c;可以完成導入&#xff0c;但也不得不說幾點問題。 下面是ArcGIS中批量導入SHP的操作界面。 比較分析 &#xff08;1&#xff09;界面簡潔性 明顯ArcGIS要簡潔…