成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

單例模式,關鍵字級別詳解

開發 前端
如果你去問一個寫過幾年代碼的程序員用過哪些設計模式,我打賭,90%以上的回答里面會帶【單例模式】。甚至有的面試官會直接問:說一下你用過哪些設計模式,單例就不用說了。你看,連面試官都聽煩了,火爆程度可見一斑。

[[413430]]

本文轉載自微信公眾號「Java課代表」,作者Java課代表 。轉載本文請聯系Java課代表公眾號。

0.前言

如果你去問一個寫過幾年代碼的程序員用過哪些設計模式,我打賭,90%以上的回答里面會帶【單例模式】。甚至有的面試官會直接問:說一下你用過哪些設計模式,單例就不用說了。你看,連面試官都聽煩了,火爆程度可見一斑。

不過,看似簡單的單例模式,里面蘊含了很多Java基礎,日常開發過程中課代表見過很多不規范的,甚至是有問題的單例實現。所以整理此文,總結一下單例模式的最佳實踐。

1、懶加載(懶漢)

所謂懶加載,就是直到第一次被調用時才加載。其實現需要考慮并發問題和指令重排,代碼如下:

  1. public class Singleton { 
  2.  
  3.     private volatile static Singleton instance; //① 
  4.  
  5.     private Singleton() { //② 
  6.     } 
  7.  
  8.     public static Singleton getInstance() { 
  9.         if (instance == null) {//③ 
  10.             synchronized (Singleton.class) { 
  11.                 if (instance == null) {//④ 
  12.                     instance = new Singleton();//⑤ 
  13.                 } 
  14.             } 
  15.         } 
  16.         return instance; 
  17.     } 

這段代碼精簡至極,沒有一個字符是多余的,下面逐行解讀一下:

首先,注意到①處的volatile關鍵字,它具備兩項特性:

一是保證此變量對于所有線程的可見性。即當一條線程修改了這個變量的值,新值對于其他線程來說是可以立即得知的。

二是禁止指令重排序優化。

這里解釋一下指令重排序優化:

代碼 ⑤ 處的instance = new Singleton();并不是原子的,大體可分為如下 3 步:

  • 分配內存
  • 調用構造函數初始化成實例
  • 讓instance指向分配的內存空間

JVM 允許在保證結果正確的前提下進行指令重排序優化。即如上 3 步可能的順序為1->2->3 或 1->3->2 。如果順序是 1->3->2 ,當 3 執行完,2 還未執行時,另一個線程執行到代碼 ③ 處,發現instance不為null,直接返回還未初始化好的instance并使用,就會報錯。

所以使用volatile,就是為了保證線程間的可見性和防止指令重排。

其次,代碼②處將構造函數聲明為private目的在于阻止使用new Singleton()這樣的代碼生成新實例。

最后,當客戶端調用Singleton.getInstance()時,先檢查是否已經實例化(代碼③),未實例化時同步代碼塊,然后再次檢查是否已實例化(代碼④),然后才執行代碼⑤。兩次檢查的意義在于,防止synchronized同步過程中其他線程進行了實例化。

這就是著名的雙重檢查鎖(Double check lock)實現單例,也即懶加載。

TIPS:

網上也有直接對getInstance()方法加鎖的版本,這樣大范圍的方法級別加鎖會導致并發變低,實際上第一次調用生成實例之后,后續獲取實例根本不需要并發控制了。而本例的雙重檢查鎖版本可以避免此并發問題。

2、預加載(餓漢)

與懶加載相對應,預加載是在類加載時就已經初始化好了,所以是天然線程安全的,代碼如下:

  1. public class Singleton { 
  2.  
  3.     private static final Singleton instance = new Singleton();// ① 
  4.      
  5.     private Singleton(){} 
  6.      
  7.     public static Singleton getInstance(){ 
  8.         return instance; 
  9.     } 

注意到 ① 處的類變量使用了final。

這里用final更多的意義在于提供語法約束。畢竟你是單例,就只有這一個實例,不可能再指向另一個。instance有了final的約束,后面再有人不小心編寫了修改其指向的代碼就會報語法錯誤。

這就好比@Override注解,你能保證寫對方法名和參數,那不寫注解也沒問題,但是有了注解的約束,編譯器就會幫你檢查,還能防止別人亂改。

3、靜態內部類

此方法和預加載原理相同,都是利用JVM類加載的特性實現天然的線程安全,不同之處在于,靜態內部類做到了延遲加載。

  1. public class Singleton { 
  2.      
  3.     private static class SingletonHolder { 
  4.         private static Singleton instance = new Singleton(); 
  5.     } 
  6.      
  7.     private Singleton(){} 
  8.  
  9.     public static Singleton getInstance() { 
  10.         return SingletonHolder.instance; 
  11.     } 

SingletonHolder 是靜態內部類,當外部類Singleton被加載的時候并不會創建任何實例,只有當Singleton.getInstance()被調用的時候,才會創建Singleton實例,這一切由 JVM 天然完成,所以既保證了線程安全,又實現了延遲加載。

4、枚舉

沒錯,枚舉可以實現單例,而且這種方式是《Effective Java中文版》第二版 中的推薦實現方式。代碼極其簡單:

  1. public enum Singleton { 
  2.     /** 
  3.      * 單例實例 
  4.      */ 
  5.     INSTANCE; 
  6.  
  7.     public void doSomeThing(){ 
  8.         System.out.println("done"); 
  9.     } 

使用時直接Singleton.INSTANCE.doSomeThing();即可。

這里主要利用了枚舉的如下兩個特性:

  • 枚舉的構造器總是私有的,所以不必像前幾種方式一樣顯式定義私有構造方法
  • 枚舉類中的每個值,都是實例(只有INSTANCE這一個實例)

除此之外,枚舉還附帶了一些額外好處:無償地提供了序列化機制,還可以防止通過多次反序列化生成多個實例。

鑒于此,單例的最佳實踐就是用枚舉來實現。

5、總結

事實上,單例的寫法并不止于本文所提的這 4 種,你可能還會看到很多其他變種,它們或多或少都存在一些缺陷,比如,懶加載方式將synchronized作用于整個方法上也能實現,但頻繁加鎖,釋放鎖會產生性能瓶頸,而完全去掉鎖又會帶來并發問題。

所以,只要吃透了文中列出的這 4 種單例方式,就能做到舉一反三,見到別人寫的單例也能一眼看出對錯。

文中所列的 4 種單例模式,除了枚舉之外,全都用到了static關鍵字,《Java 虛擬機規范》 規定,有幾種情況必須立即對類進行“初始化”,其中涉及static的場景如下:

讀取或設置一個類型的靜態字段(被 final 修飾、已在編譯期把結果放入常量池的靜態字段除外)的時候。

調用一個類型的靜態方法的時候。

懶加載,預加載和靜態內部類正是利用了這兩點特性。

對static關鍵字遺忘的同學可以參看我的另一篇文章:《一題搞定static關鍵字》

最后,再次強調一下,如果大家開發中需要手寫單例,建議聽從 Joshua Bloch在《Effective Java中文版》第二版 中的建議:

單元素的枚舉類型已經成為實現 Singleton 的最佳方法

參考資料:

1、《Effective Java中文版》 Joshua Bloch 第二版 P15

2、《深入理解 Java 虛擬機》 周志明 第3版,P444-P448,P264

 

3、深入淺出單實例SINGLETON設計模式 (https://coolshell.cn/articles/265.html)

 

責任編輯:武曉燕 來源: Java課代表
相關推薦

2020-12-28 13:22:47

單例模式關鍵字

2020-12-29 05:31:33

單例模式this關鍵字

2009-09-02 09:24:03

C# this關鍵字

2013-01-30 10:12:14

Pythonyield

2021-02-01 13:10:07

Staticc語言UNIX系統

2011-06-14 13:26:27

volatile

2022-11-12 18:32:50

Golangomitemptyjson

2009-09-28 11:34:49

Javascript

2025-06-13 08:00:00

Java并發編程volatile

2011-06-28 15:18:45

Qt 單例模式

2024-03-15 11:52:03

C++關鍵字編程

2017-05-27 20:59:30

Java多線程synchronize

2010-02-05 15:51:06

C++ explici

2023-03-09 07:38:58

static關鍵字狀態

2023-11-10 09:29:30

MySQLExplain

2010-02-05 17:00:06

C++單例模式

2011-06-27 17:32:20

2021-03-02 08:50:31

設計單例模式

2021-02-01 10:01:58

設計模式 Java單例模式

2009-09-17 09:30:00

Linq LET關鍵字
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 成人a视频| 成人小视频在线免费观看 | 91福利电影在线观看 | 九九av | 国产高清久久久 | 欧美日韩在线一区二区三区 | 国产精品久久久久久福利一牛影视 | 免费一级黄色录像 | 亚洲精品乱码久久久久v最新版 | 精品精品视频 | 91九色在线观看 | 国产玖玖| 久久久久国产精品www | 91精品国产91久久久久久最新 | 成人精品一区二区 | 中文字幕免费中文 | 午夜精品久久久久久 | 午夜精品福利视频 | 日韩精品一区二区三区 | 91精品国产一二三 | 91xh98hx 在线 国产 | 亚洲协和影视 | 欧美一级α片 | 艹逼网| 亚洲视频不卡 | 一级免费看 | 欧美黄色录像 | 男女羞羞网站 | 亚洲精品中文字幕在线观看 | 成人免费一级 | 欧美一区二区二区 | 精品国产青草久久久久96 | 先锋资源网 | 一级毛片网| 精品在线视频播放 | 在线观看成人av | 中文字幕国产视频 | 国产欧美精品一区 | 一级片免费视频 | 欧美一级黄色免费看 | 国产91网站在线观看 |