高併發下線程安全的單例模式

複製來自 http://blog.csdn.net/cselmu9/article/details/51366946java

在全部的設計模式中,單例模式是咱們在項目開發中最爲常見的設計模式之一,而單例模式有不少種實現方式,你是否都瞭解呢?高併發下如何保證單例模式的線程安全性呢?如何保證序列化後的單例對象在反序列化後任然是單例的呢?這些問題在看了本文以後都會一一的告訴你答案,趕快來閱讀吧!mysql

什麼是單例模式?

在文章開始以前咱們仍是有必要介紹一下什麼是單例模式。單例模式是爲確保一個類只有一個實例,併爲整個系統提供一個全局訪問點的一種模式方法。sql

從概念中體現出了單例的一些特色:數據庫

(1)、在任何狀況下,單例類永遠只有一個實例存在編程

(2)、單例須要有能力爲整個系統提供這一惟一實例  設計模式

 

爲了便於讀者更好的理解這些概念,下面給出這麼一段內容敘述:緩存

在計算機系統中,線程池、緩存、日誌對象、對話框、打印機、顯卡的驅動程序對象常被設計成單例。這些應用都或多或少具備資源管理器的功能。每臺計算機能夠有若干個打印機,但只能有一個Printer Spooler,以免兩個打印做業同時輸出到打印機中。每臺計算機能夠有若干通訊端口,系統應當集中管理這些通訊端口,以免一個通訊端口同時被兩個請求同時調用。總之,選擇單例模式就是爲了不不一致狀態,避免政出多頭。

正是因爲這個特色,單例對象一般做爲程序中的存放配置信息的載體,由於它能保證其餘對象讀到一致的信息。例如在某個服務器程序中,該服務器的配置信息可能存放在數據庫或文件中,這些配置數據由某個單例對象統一讀取,服務進程中的其餘對象若是要獲取這些配置信息,只需訪問該單例對象便可。這種方式極大地簡化了在複雜環境 下,尤爲是多線程環境下的配置管理,可是隨着應用場景的不一樣,也可能帶來一些同步問題。
    安全

各式各樣的單例實現

舒適提示:本文敘述中涉及到的相關源碼能夠在這裏進行下載源碼,讀者可免積分下載。服務器

一、餓漢式單例

餓漢式單例是指在方法調用前,實例就已經建立好了。下面是實現代碼:多線程

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s01;  
  2.   
  3. public class MySingleton {  
  4.       
  5.     private static MySingleton instance = new MySingleton();  
  6.       
  7.     private MySingleton(){}  
  8.       
  9.     public static MySingleton getInstance() {  
  10.         return instance;  
  11.     }  
  12.       
  13. }  

以上是單例的餓漢式實現,咱們來看看餓漢式在多線程下的執行狀況,給出一段多線程的執行代碼:

 

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s01;  
  2.   
  3. public class MyThread extends Thread{  
  4.       
  5.     @Override  
  6.     public void run() {   
  7.         System.out.println(MySingleton.getInstance().hashCode());  
  8.     }  
  9.       
  10.     public static void main(String[] args) {   
  11.           
  12.         MyThread[] mts = new MyThread[10];  
  13.         for(int i = 0 ; i < mts.length ; i++){  
  14.             mts[i] = new MyThread();  
  15.         }  
  16.           
  17.         for (int j = 0; j < mts.length; j++) {  
  18.             mts[j].start();  
  19.         }  
  20.     }  
  21. }  

以上代碼運行結果:

 

[plain]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. 1718900954  
  2. 1718900954  
  3. 1718900954  
  4. 1718900954  
  5. 1718900954  
  6. 1718900954  
  7. 1718900954  
  8. 1718900954  
  9. 1718900954  
  10. 1718900954  


從運行結果能夠看出實例變量額hashCode值一致,這說明對象是同一個,餓漢式單例實現了。

 

 

二、懶漢式單例

懶漢式單例是指在方法調用獲取實例時才建立實例,由於相對餓漢式顯得「不急迫」,因此被叫作「懶漢模式」。下面是實現代碼:

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s02;  
  2.   
  3. public class MySingleton {  
  4.       
  5.     private static MySingleton instance = null;  
  6.       
  7.     private MySingleton(){}  
  8.       
  9.     public static MySingleton getInstance() {  
  10.         if(instance == null){//懶漢式  
  11.             instance = new MySingleton();  
  12.         }  
  13.         return instance;  
  14.     }  
  15. }  

這裏實現了懶漢式的單例,可是熟悉多線程併發編程的朋友應該能夠看出,在多線程併發下這樣的實現是沒法保證明例實例惟一的,甚至能夠說這樣的失效是徹底錯誤的,下面咱們就來看一下多線程併發下的執行狀況,這裏爲了看到效果,咱們對上面的代碼作一小點修改:

 

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s02;  
  2.   
  3. public class MySingleton {  
  4.       
  5.     private static MySingleton instance = null;  
  6.       
  7.     private MySingleton(){}  
  8.       
  9.     public static MySingleton getInstance() {  
  10.         try {   
  11.             if(instance != null){//懶漢式   
  12.                   
  13.             }else{  
  14.                 //建立實例以前可能會有一些準備性的耗時工做   
  15.                 Thread.sleep(300);  
  16.                 instance = new MySingleton();  
  17.             }  
  18.         } catch (InterruptedException e) {   
  19.             e.printStackTrace();  
  20.         }  
  21.         return instance;  
  22.     }  
  23. }  

這裏假設在建立實例前有一些準備性的耗時工做要處理,多線程調用:

 

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s02;  
  2.   
  3. public class MyThread extends Thread{  
  4.       
  5.     @Override  
  6.     public void run() {   
  7.         System.out.println(MySingleton.getInstance().hashCode());  
  8.     }  
  9.       
  10.     public static void main(String[] args) {   
  11.           
  12.         MyThread[] mts = new MyThread[10];  
  13.         for(int i = 0 ; i < mts.length ; i++){  
  14.             mts[i] = new MyThread();  
  15.         }  
  16.           
  17.         for (int j = 0; j < mts.length; j++) {  
  18.             mts[j].start();  
  19.         }  
  20.     }  
  21. }  

執行結果以下:

 

[plain]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. 1210420568  
  2. 1210420568  
  3. 1935123450  
  4. 1718900954  
  5. 1481297610  
  6. 1863264879  
  7. 369539795  
  8. 1210420568  
  9. 1210420568  
  10. 602269801  


從這裏執行結果能夠看出,單例的線程安全性並無獲得保證,那要怎麼解決呢?

 

 

三、線程安全的懶漢式單例

要保證線程安全,咱們就得須要使用同步鎖機制,下面就來看看咱們如何一步步的解決 存在線程安全問題的懶漢式單例(錯誤的單例)。

(1)、 方法中聲明synchronized關鍵字

出現非線程安全問題,是因爲多個線程能夠同時進入getInstance()方法,那麼只須要對該方法進行synchronized的鎖同步便可:

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s03;  
  2.   
  3. public class MySingleton {  
  4.       
  5.     private static MySingleton instance = null;  
  6.       
  7.     private MySingleton(){}  
  8.       
  9.     public synchronized static MySingleton getInstance() {  
  10.         try {   
  11.             if(instance != null){//懶漢式   
  12.                   
  13.             }else{  
  14.                 //建立實例以前可能會有一些準備性的耗時工做   
  15.                 Thread.sleep(300);  
  16.                 instance = new MySingleton();  
  17.             }  
  18.         } catch (InterruptedException e) {   
  19.             e.printStackTrace();  
  20.         }  
  21.         return instance;  
  22.     }  
  23. }  

此時任然使用前面驗證多線程下執行狀況的MyThread類來進行驗證,將其放入到org.mlinge.s03包下運行,執行結果以下:

 

[plain]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. 1689058373  
  2. 1689058373  
  3. 1689058373  
  4. 1689058373  
  5. 1689058373  
  6. 1689058373  
  7. 1689058373  
  8. 1689058373  
  9. 1689058373  
  10. 1689058373  

 

從執行結果上來看,問題已經解決了,可是這種實現方式的運行效率會很低。同步方法效率低,那咱們考慮使用同步代碼塊來實現:

 

(2)、 同步代碼塊實現

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s03;  
  2.   
  3. public class MySingleton {  
  4.       
  5.     private static MySingleton instance = null;  
  6.       
  7.     private MySingleton(){}  
  8.       
  9.     //public synchronized static MySingleton getInstance() {  
  10.     public static MySingleton getInstance() {  
  11.         try {   
  12.             synchronized (MySingleton.class) {  
  13.                 if(instance != null){//懶漢式   
  14.                       
  15.                 }else{  
  16.                     //建立實例以前可能會有一些準備性的耗時工做   
  17.                     Thread.sleep(300);  
  18.                     instance = new MySingleton();  
  19.                 }  
  20.             }  
  21.         } catch (InterruptedException e) {   
  22.             e.printStackTrace();  
  23.         }  
  24.         return instance;  
  25.     }  
  26. }  

這裏的實現可以保證多線程併發下的線程安全性,可是這樣的實現將所有的代碼都被鎖上了,一樣的效率很低下。

 

(3)、 針對某些重要的代碼來進行單獨的同步(可能非線程安全)

針對某些重要的代碼進行單獨的同步,而不是所有進行同步,能夠極大的提升執行效率,咱們來看一下:

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s04;  
  2.   
  3. public class MySingleton {  
  4.       
  5.     private static MySingleton instance = null;  
  6.       
  7.     private MySingleton(){}  
  8.        
  9.     public static MySingleton getInstance() {  
  10.         try {    
  11.             if(instance != null){//懶漢式   
  12.                   
  13.             }else{  
  14.                 //建立實例以前可能會有一些準備性的耗時工做   
  15.                 Thread.sleep(300);  
  16.                 synchronized (MySingleton.class) {  
  17.                     instance = new MySingleton();  
  18.                 }  
  19.             }   
  20.         } catch (InterruptedException e) {   
  21.             e.printStackTrace();  
  22.         }  
  23.         return instance;  
  24.     }  
  25. }  

此時一樣使用前面驗證多線程下執行狀況的MyThread類來進行驗證,將其放入到org.mlinge.s04包下運行,執行結果以下:

[plain]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. 1481297610  
  2. 397630378  
  3. 1863264879  
  4. 1210420568  
  5. 1935123450  
  6. 369539795  
  7. 590202901  
  8. 1718900954  
  9. 1689058373  
  10. 602269801  

從運行結果來看,這樣的方法進行代碼塊同步,代碼的運行效率是可以獲得提高,可是卻沒能保住線程的安全性。看來還得進一步考慮如何解決此問題。

 

(4)、 Double Check Locking 雙檢查鎖機制(推薦)

爲了達到線程安全,又能提升代碼執行效率,咱們這裏能夠採用DCL的雙檢查鎖機制來完成,代碼實現以下:

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s05;  
  2.   
  3. public class MySingleton {  
  4.       
  5.     //使用volatile關鍵字保其可見性  
  6.     volatile private static MySingleton instance = null;  
  7.       
  8.     private MySingleton(){}  
  9.        
  10.     public static MySingleton getInstance() {  
  11.         try {    
  12.             if(instance != null){//懶漢式   
  13.                   
  14.             }else{  
  15.                 //建立實例以前可能會有一些準備性的耗時工做   
  16.                 Thread.sleep(300);  
  17.                 synchronized (MySingleton.class) {  
  18.                     if(instance == null){//二次檢查  
  19.                         instance = new MySingleton();  
  20.                     }  
  21.                 }  
  22.             }   
  23.         } catch (InterruptedException e) {   
  24.             e.printStackTrace();  
  25.         }  
  26.         return instance;  
  27.     }  
  28. }  

將前面驗證多線程下執行狀況的MyThread類放入到org.mlinge.s05包下運行,執行結果以下:

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. 369539795  
  2. 369539795  
  3. 369539795  
  4. 369539795  
  5. 369539795  
  6. 369539795  
  7. 369539795  
  8. 369539795  
  9. 369539795  
  10. 369539795  

從運行結果來看,該中方法保證了多線程併發下的線程安全性。

 

這裏在聲明變量時使用了volatile關鍵字來保證其線程間的可見性;在同步代碼塊中使用二次檢查,以保證其不被重複實例化。集合其兩者,這種實現方式既保證了其高效性,也保證了其線程安全性。

四、使用靜態內置類實現單例模式

DCL解決了多線程併發下的線程安全問題,其實使用其餘方式也能夠達到一樣的效果,代碼實現以下:

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s06;  
  2.   
  3. public class MySingleton {  
  4.       
  5.     //內部類  
  6.     private static class MySingletonHandler{  
  7.         private static MySingleton instance = new MySingleton();  
  8.     }   
  9.       
  10.     private MySingleton(){}  
  11.        
  12.     public static MySingleton getInstance() {   
  13.         return MySingletonHandler.instance;  
  14.     }  
  15. }  

以上代碼就是使用靜態內置類實現了單例模式,這裏將前面驗證多線程下執行狀況的MyThread類放入到org.mlinge.s06包下運行,執行結果以下:

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. 1718900954  
  2. 1718900954  
  3. 1718900954  
  4. 1718900954  
  5. 1718900954  
  6. 1718900954  
  7. 1718900954  
  8. 1718900954  
  9. 1718900954  
  10. 1718900954  

從運行結果來看,靜態內部類實現的單例在多線程併發下單個實例獲得了保證。

 

五、序列化與反序列化的單例模式實現

靜態內部類雖然保證了單例在多線程併發下的線程安全性,可是在遇到序列化對象時,默認的方式運行獲得的結果就是多例的。

代碼實現以下:

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s07;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. public class MySingleton implements Serializable {  
  6.        
  7.     private static final long serialVersionUID = 1L;  
  8.   
  9.     //內部類  
  10.     private static class MySingletonHandler{  
  11.         private static MySingleton instance = new MySingleton();  
  12.     }   
  13.       
  14.     private MySingleton(){}  
  15.        
  16.     public static MySingleton getInstance() {   
  17.         return MySingletonHandler.instance;  
  18.     }  
  19. }  

序列化與反序列化測試代碼:

 

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s07;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileInputStream;  
  5. import java.io.FileNotFoundException;  
  6. import java.io.FileOutputStream;  
  7. import java.io.IOException;  
  8. import java.io.ObjectInputStream;  
  9. import java.io.ObjectOutputStream;  
  10.   
  11. public class SaveAndReadForSingleton {  
  12.       
  13.     public static void main(String[] args) {  
  14.         MySingleton singleton = MySingleton.getInstance();  
  15.           
  16.         File file = new File("MySingleton.txt");  
  17.           
  18.         try {  
  19.             FileOutputStream fos = new FileOutputStream(file);  
  20.             ObjectOutputStream oos = new ObjectOutputStream(fos);  
  21.             oos.writeObject(singleton);  
  22.             fos.close();  
  23.             oos.close();  
  24.             System.out.println(singleton.hashCode());  
  25.         } catch (FileNotFoundException e) {   
  26.             e.printStackTrace();  
  27.         } catch (IOException e) {   
  28.             e.printStackTrace();  
  29.         }  
  30.           
  31.         try {  
  32.             FileInputStream fis = new FileInputStream(file);  
  33.             ObjectInputStream ois = new ObjectInputStream(fis);  
  34.             MySingleton rSingleton = (MySingleton) ois.readObject();  
  35.             fis.close();  
  36.             ois.close();  
  37.             System.out.println(rSingleton.hashCode());  
  38.         } catch (FileNotFoundException e) {   
  39.             e.printStackTrace();  
  40.         } catch (IOException e) {   
  41.             e.printStackTrace();  
  42.         } catch (ClassNotFoundException e) {   
  43.             e.printStackTrace();  
  44.         }  
  45.           
  46.     }  
  47. }  

運行以上代碼,獲得的結果以下:

 

 

[sql]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. 865113938  
  2. 1442407170  

從結果中咱們發現,序列號對象的hashCode和反序列化後獲得的對象的hashCode值不同,說明反序列化後返回的對象是從新實例化的,單例被破壞了。那怎麼來解決這一問題呢?

 

解決辦法就是在反序列化的過程當中使用readResolve()方法,單例實現的代碼以下:

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s07;  
  2.   
  3. import java.io.ObjectStreamException;  
  4. import java.io.Serializable;  
  5.   
  6. public class MySingleton implements Serializable {  
  7.        
  8.     private static final long serialVersionUID = 1L;  
  9.   
  10.     //內部類  
  11.     private static class MySingletonHandler{  
  12.         private static MySingleton instance = new MySingleton();  
  13.     }   
  14.       
  15.     private MySingleton(){}  
  16.        
  17.     public static MySingleton getInstance() {   
  18.         return MySingletonHandler.instance;  
  19.     }  
  20.       
  21.     //該方法在反序列化時會被調用,該方法不是接口定義的方法,有點兒約定俗成的感受  
  22.     protected Object readResolve() throws ObjectStreamException {  
  23.         System.out.println("調用了readResolve方法!");  
  24.         return MySingletonHandler.instance;   
  25.     }  
  26. }  

再次運行上面的測試代碼,獲得的結果以下:

 

 

[plain]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. 865113938  
  2. 調用了readResolve方法!  
  3. 865113938  

從運行結果可知,添加readResolve方法後反序列化後獲得的實例和序列化前的是同一個實例,單個實例獲得了保證。

 

六、使用static代碼塊實現單例

靜態代碼塊中的代碼在使用類的時候就已經執行了,因此能夠應用靜態代碼塊的這個特性的實現單例設計模式。

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s08;  
  2.   
  3. public class MySingleton{  
  4.        
  5.     private static MySingleton instance = null;  
  6.        
  7.     private MySingleton(){}  
  8.   
  9.     static{  
  10.         instance = new MySingleton();  
  11.     }  
  12.       
  13.     public static MySingleton getInstance() {   
  14.         return instance;  
  15.     }   
  16. }  

測試代碼以下:

 

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s08;  
  2.   
  3. public class MyThread extends Thread{  
  4.       
  5.     @Override  
  6.     public void run() {   
  7.         for (int i = 0; i < 5; i++) {  
  8.             System.out.println(MySingleton.getInstance().hashCode());  
  9.         }  
  10.     }  
  11.       
  12.     public static void main(String[] args) {   
  13.           
  14.         MyThread[] mts = new MyThread[3];  
  15.         for(int i = 0 ; i < mts.length ; i++){  
  16.             mts[i] = new MyThread();  
  17.         }  
  18.           
  19.         for (int j = 0; j < mts.length; j++) {  
  20.             mts[j].start();  
  21.         }  
  22.     }  
  23. }  

運行結果以下:

 

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. 1718900954  
  2. 1718900954  
  3. 1718900954  
  4. 1718900954  
  5. 1718900954  
  6. 1718900954  
  7. 1718900954  
  8. 1718900954  
  9. 1718900954  
  10. 1718900954  
  11. 1718900954  
  12. 1718900954  
  13. 1718900954  
  14. 1718900954  
  15. 1718900954  

從運行結果看,單例的線程安全性獲得了保證。

 

七、使用枚舉數據類型實現單例模式

枚舉enum和靜態代碼塊的特性類似,在使用枚舉時,構造方法會被自動調用,利用這一特性也能夠實現單例:

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s09;  
  2.   
  3. public enum EnumFactory{   
  4.       
  5.     singletonFactory;  
  6.       
  7.     private MySingleton instance;  
  8.       
  9.     private EnumFactory(){//枚舉類的構造方法在類加載是被實例化  
  10.         instance = new MySingleton();  
  11.     }  
  12.           
  13.     public MySingleton getInstance(){  
  14.         return instance;  
  15.     }  
  16.       
  17. }  
  18.   
  19. class MySingleton{//須要獲實現單例的類,好比數據庫鏈接Connection  
  20.     public MySingleton(){}   
  21. }  

測試代碼以下:

 

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s09;  
  2.   
  3. public class MyThread extends Thread{  
  4.       
  5.     @Override  
  6.     public void run() {   
  7.         System.out.println(EnumFactory.singletonFactory.getInstance().hashCode());  
  8.     }  
  9.       
  10.     public static void main(String[] args) {   
  11.           
  12.         MyThread[] mts = new MyThread[10];  
  13.         for(int i = 0 ; i < mts.length ; i++){  
  14.             mts[i] = new MyThread();  
  15.         }  
  16.           
  17.         for (int j = 0; j < mts.length; j++) {  
  18.             mts[j].start();  
  19.         }  
  20.     }  
  21. }  

執行後獲得的結果:

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. 1481297610  
  2. 1481297610  
  3. 1481297610  
  4. 1481297610  
  5. 1481297610  
  6. 1481297610  
  7. 1481297610  
  8. 1481297610  
  9. 1481297610  
  10. 1481297610  

運行結果代表單例獲得了保證,可是這樣寫枚舉類被徹底暴露了,聽說違反了「職責單一原則」,那咱們來看看怎麼進行改造呢。

 

八、完善使用enum枚舉實現單例模式

不暴露枚舉類實現細節的封裝代碼以下:

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s10;  
  2.   
  3. public class ClassFactory{   
  4.       
  5.     private enum MyEnumSingleton{  
  6.         singletonFactory;  
  7.           
  8.         private MySingleton instance;  
  9.           
  10.         private MyEnumSingleton(){//枚舉類的構造方法在類加載是被實例化  
  11.             instance = new MySingleton();  
  12.         }  
  13.    
  14.         public MySingleton getInstance(){  
  15.             return instance;  
  16.         }  
  17.     }   
  18.    
  19.     public static MySingleton getInstance(){  
  20.         return MyEnumSingleton.singletonFactory.getInstance();  
  21.     }  
  22. }  
  23.   
  24. class MySingleton{//須要獲實現單例的類,好比數據庫鏈接Connection  
  25.     public MySingleton(){}   
  26. }  

驗證單例實現的代碼以下:

 

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. package org.mlinge.s10;  
  2.   
  3. public class MyThread extends Thread{  
  4.       
  5.     @Override  
  6.     public void run() {   
  7.         System.out.println(ClassFactory.getInstance().hashCode());  
  8.     }  
  9.       
  10.     public static void main(String[] args) {   
  11.           
  12.         MyThread[] mts = new MyThread[10];  
  13.         for(int i = 0 ; i < mts.length ; i++){  
  14.             mts[i] = new MyThread();  
  15.         }  
  16.           
  17.         for (int j = 0; j < mts.length; j++) {  
  18.             mts[j].start();  
  19.         }  
  20.     }  
  21. }  

驗證結果:

 

 

[java]  view plain  copy
 
 print?在CODE上查看代碼片派生到個人代碼片
  1. 1935123450  
  2. 1935123450  
  3. 1935123450  
  4. 1935123450  
  5. 1935123450  
  6. 1935123450  
  7. 1935123450  
  8. 1935123450  
  9. 1935123450  
  10. 1935123450  

驗證結果代表,完善後的單例實現更爲合理。

 

以上就是本文要介紹的全部單例模式的實現,相信認真閱讀的讀者都已經明白文章開頭所引入的那幾個問題了,祝你們讀得開心:-D!

 

備註:本文的編寫思路和實例源碼參照《Java多線程編程核心技術》-(高洪巖)一書中第六章的學習案例撰寫。

相關文章
相關標籤/搜索