單例模式-多線程環境


單例模式-多線程環境

 

  單例-立即加載:

 1 /**
 2  *    單例模式,立即加載
 3  */
 4 public class MyObject {
 5     
 6     private static MyObject myObject = new MyObject();//立即加載(類加載的時候就已經實例化好了)
 7     
 8     private MyObject() {}
 9     
10     public static MyObject getInstance() {
11         /**
12          *     此代碼版本為立即加載
13          *     缺點是不能有其他實例變量,因為getInstance()方法沒有同步,所有可能出現非線程安全問題
14          */
15         return myObject;
16     }
17 }
 1 /**
 2  *    線程類
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }
 1 /**
 2  *    測試類,創建三個線程分別去獲取實例並輸出hashCode值
 3  *        通過結果可以看出三個線程打印的hashCode值相同,說明對象是同一個,並且實現了立即加載型單例設計模式
 4  */
 5 public class Run {
 6 
 7     public static void main(String[] args) {
 8         MyThread t1 = new MyThread();
 9         MyThread t2 = new MyThread();
10         MyThread t3 = new MyThread();
11 
12         t1.start();
13         t2.start();
14         t3.start();
15     }
16 }

  單例-延遲加載:(該版本單例模式,如果在多線程環境,則可能會出現多個實例)

 1 /**
 2  *    單例設計類,延遲加載,當需要用再創建實例
 3  */
 4 public class MyObject {
 5     
 6     private static MyObject myObject;
 7     
 8     private MyObject() {}
 9     
10     public static MyObject getInstance() {
11         if(myObject != null) {//延遲加載,也就是當用到了再創建
12         }else {
13             myObject = new MyObject();
14         }
15         return myObject;
16     }
17 }
 1 /**
 2  *    線程類
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }
 1 /**
 2  *    測試類
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    如果是在多線程環境,會出現多個實例的情況,此時就與單例模式的初衷相違背了
 8      */
 9     public static void main(String[] args) {
10         MyThread t1 = new MyThread();
11         t1.start();
12     }
13 }

  演示:延遲單例模式,出現多個實例

 1 /**
 2  *    延遲加載單例模式類
 3  */
 4 public class MyObject {
 5     
 6     private static MyObject myObject;
 7     
 8     private MyObject() {}
 9     
10     public static MyObject getInstance() {
11         try {
12             if(myObject != null) {
13             }else {
14                 Thread.sleep(3000);//模擬在創建對象之前做一些准備性工作
15                 myObject = new MyObject();
16             }
17         } catch (InterruptedException e) {
18             e.printStackTrace();
19         }
20         return myObject;
21     }
22 }
 1 /**
 2  *    線程類
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }
 1 /**
 2  *    測試類
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    通過運行結果可以看到打印的三個線程獲取的對象的hashCode值不同,也就是出現了多例
 8      */
 9     public static void main(String[] args) {
10         MyThread t1 = new MyThread();
11         MyThread t2 = new MyThread();
12         MyThread t3 = new MyThread();
13         t1.start();
14         t2.start();
15         t3.start();
16     }
17 }

  優化1:優化出現多例的情況,將整個方法加上同步鎖

 1 /**
 2  *    延遲加載單例模式類
 3  */
 4 public class MyObject {
 5     
 6     private static MyObject myObject;
 7     
 8     private MyObject() {}
 9     
10     //防止多線程出現多例,此處將整個方法加上同步鎖(效率低)
11     synchronized public static MyObject getInstance() {
12         try {
13             if(myObject != null) {
14             }else {
15                 Thread.sleep(3000);//模擬在創建對象之前做一些准備性工作
16                 myObject = new MyObject();
17             }
18         } catch (InterruptedException e) {
19             e.printStackTrace();
20         }
21         return myObject;
22     }
23 }
 1 /**
 2  *    線程類
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }
 1 /**
 2  *    測試類
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    測試結果可以看出是同步運行,但是整個方法加上同步鎖,效率太低
 8      */
 9     public static void main(String[] args) {
10         MyThread t1 = new MyThread();
11         MyThread t2 = new MyThread();
12         MyThread t3 = new MyThread();
13         t1.start();
14         t2.start();
15         t3.start();
16     }
17 }

  再次優化:使用同步代碼塊

 1 /**
 2  *    延遲加載單例模式類
 3  */
 4 public class MyObject {
 5     
 6     private static MyObject myObject;
 7     
 8     private MyObject() {}
 9     
10     //防止多線程出現多例,在使用同步代碼塊
11     public static MyObject getInstance() {
12         try {
13             synchronized(MyObject.class) {
14                 if(myObject != null) {
15                 }else {
16                     Thread.sleep(3000);//模擬在創建對象之前做一些准備性工作
17                     myObject = new MyObject();
18                 }
19             }
20         } catch (InterruptedException e) {
21             e.printStackTrace();
22         }
23         return myObject;
24     }
25 }
 1 /**
 2  *    線程類
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }
 1 /**
 2  *    測試類
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    測試結果可以看出是同步運行,修改成使用同步代碼塊,效率也是低因為整個方法的代碼都在同步塊中
 8      */
 9     public static void main(String[] args) {
10         MyThread t1 = new MyThread();
11         MyThread t2 = new MyThread();
12         MyThread t3 = new MyThread();
13         t1.start();
14         t2.start();
15         t3.start();
16     }
17 }

  繼續優化:針對重要代碼使用同步代碼塊

 1 /**
 2  *    延遲加載單例模式類
 3  */
 4 public class MyObject {
 5     
 6     private static MyObject myObject;
 7     
 8     private MyObject() {}
 9     
10     public static MyObject getInstance() {
11         try {
12             if(myObject != null) {
13             }else {
14                 Thread.sleep(3000);//模擬在創建對象之前做一些准備性工作
15                 //雖然部分代碼上鎖,但是還是存在非線程安全問題
16                 synchronized(MyObject.class) {
17                     myObject = new MyObject();
18                 }
19             }
20         } catch (InterruptedException e) {
21             e.printStackTrace();
22         }
23         return myObject;
24     }
25 }
 1 /**
 2  *    線程類
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }
 1 /**
 2  *    測試類
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    測試結果可以看出得到多個實例
 8      *        該優化只對實例對象的關鍵代碼進行同步,結構上來說效率提升了,但是遇到多線程,還是無法解決得到同一個實例對象
 9      */
10     public static void main(String[] args) {
11         MyThread t1 = new MyThread();
12         MyThread t2 = new MyThread();
13         MyThread t3 = new MyThread();
14         t1.start();
15         t2.start();
16         t3.start();
17     }
18 }

  終極優化:使用DCL雙檢查鎖機制實現多線程中延遲加載單例設計模式,解決出現多例的情況以及效率問題

 1 /**
 2  *    延遲加載單例模式類
 3  */
 4 public class MyObject {
 5     //使用volatile關鍵字
 6     private volatile static MyObject myObject;
 7     
 8     private MyObject() {}
 9     
10     public static MyObject getInstance() {
11         try {
12             if(myObject != null) {
13             }else {
14                 Thread.sleep(3000);//模擬在創建對象之前做一些准備性工作
15                 synchronized(MyObject.class) {
16                     if(myObject == null) {
17                         myObject = new MyObject();
18                     }
19                 }
20             }
21         } catch (InterruptedException e) {
22             e.printStackTrace();
23         }
24         return myObject;
25     }
26 }
 1 /**
 2  *    線程類
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }
 1 /**
 2  *    測試類
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    測試結果可以看出多個線程獲得的是同一個對象
 8      *        使用雙重檢查鎖功能,解決懶漢模式遇到的多線程問題
 9      */
10     public static void main(String[] args) {
11         MyThread t1 = new MyThread();
12         MyThread t2 = new MyThread();
13         MyThread t3 = new MyThread();
14         t1.start();
15         t2.start();
16         t3.start();
17     }
18 }

  使用靜態內部類實現單例:

 1 /**
 2  *    延遲加載單例模式類
 3  */
 4 public class MyObject {
 5     
 6     //靜態內部類
 7     public static class MyObjectHandler{
 8         private static MyObject myObject = new MyObject();
 9     }
10     
11     private MyObject() {}
12     
13     public static MyObject getInstance() {
14         return MyObjectHandler.myObject;
15     }
16 }
 1 /**
 2  *    線程類
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }
 1 /**
 2  *    測試類
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    測試結果可以看出多個線程獲得的是同一個對象
 8      */
 9     public static void main(String[] args) {
10         MyThread t1 = new MyThread();
11         MyThread t2 = new MyThread();
12         MyThread t3 = new MyThread();
13         t1.start();
14         t2.start();
15         t3.start();
16     }
17 }

  序列化與反序列化的單例設計模式:

 1 import java.io.Serializable;
 2 
 3 /**
 4  *    延遲加載單例模式類
 5  */
 6 public class MyObject implements Serializable{
 7     
 8     private static final long serialVersionUID = 1L;
 9 
10     //靜態內部類
11     public static class MyObjectHandler{
12         private static MyObject myObject = new MyObject();
13     }
14     
15     private MyObject() {}
16     
17     public static MyObject getInstance() {
18         return MyObjectHandler.myObject;
19     }
20     //保證反序列化拿到的對象與序列化對象是同一個對象
21     protected Object readResolve() {
22         System.out.println("調用了readResolve()方法");
23         return MyObjectHandler.myObject;
24     }
25 }
 1 import java.io.File;
 2 import java.io.FileInputStream;
 3 import java.io.FileNotFoundException;
 4 import java.io.FileOutputStream;
 5 import java.io.IOException;
 6 import java.io.ObjectInputStream;
 7 import java.io.ObjectOutputStream;
 8 
 9 /**
10  *    測試類
11  */
12 public class Run {
13     public static void main(String[] args) {
14         //將對象序列化到文件
15         try {
16             MyObject myObject = MyObject.getInstance();
17             FileOutputStream fops = new FileOutputStream(new File("myObjectFile.text"));
18             ObjectOutputStream oops = new ObjectOutputStream(fops);
19             oops.writeObject(myObject);
20             oops.close();
21             fops.close();
22             System.out.println(myObject.hashCode());
23         } catch (FileNotFoundException e) {
24             e.printStackTrace();
25         } catch (IOException e) {
26             e.printStackTrace();
27         }
28         //將文件中序列化的對象反序列化並輸出到控制台
29         try {
30             FileInputStream fips = new FileInputStream(new File("myObjectFile.text"));
31             ObjectInputStream oips = new ObjectInputStream(fips);
32             MyObject readObject = (MyObject)oips.readObject();
33             oips.close();
34             fips.close();
35             System.out.println(readObject.hashCode());
36         } catch (FileNotFoundException e) {
37             e.printStackTrace();
38         } catch (IOException e) {
39             e.printStackTrace();
40         } catch (ClassNotFoundException e) {
41             e.printStackTrace();
42         }
43     }
44 }

  使用靜態代碼塊實現單例:

 1 /**
 2  *    延遲加載單例模式類
 3  */
 4 public class MyObject {
 5     
 6     private static MyObject instance = null;
 7     
 8     private MyObject() {}
 9     
10     static {
11         instance = new MyObject();
12     }
13     
14     public static MyObject getInstance() {
15         return instance;
16     }
17 }
 1 /**
 2  *    線程類
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         for (int i = 0; i < 5; i++) {
 9             System.out.println(MyObject.getInstance().hashCode());
10         }
11     }
12 }
 1 /**
 2  *    測試類,測試使用靜態代碼塊實現單例
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    測試結果可以看出多個線程獲得的是同一個對象
 8      */
 9     public static void main(String[] args) {
10         MyThread t1 = new MyThread();
11         MyThread t2 = new MyThread();
12         MyThread t3 = new MyThread();
13         t1.start();
14         t2.start();
15         t3.start();
16     }
17 }

  使用enum枚舉數據類型實現單例模式:

 1 import java.sql.Connection;
 2 import java.sql.DriverManager;
 3 import java.sql.SQLException;
 4 
 5 /**
 6  *    使用enum枚舉類型實現單例模式,此處的枚舉類進行了暴露,違反了職責單一原則
 7  */
 8 public enum MyObject {
 9     
10     connectionFactory;
11     
12     private Connection connection;
13     
14     private MyObject() {
15         try {
16             System.out.println("調用MyObject的構造方法");
17             String url = "";
18             String user = "";
19             String password = "";
20             String driverName = "";
21             Class.forName(driverName);
22             connection = DriverManager.getConnection(url, user, password);
23         } catch (ClassNotFoundException e) {
24             e.printStackTrace();
25         } catch (SQLException e) {
26             e.printStackTrace();
27         }
28     }
29     
30     public Connection getConnection() {
31         return connection;
32     }
33 }
 1 /**
 2  *    線程類
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         for (int i = 0; i < 5; i++) {
 9             System.out.println(MyObject.connectionFactory.getConnection().hashCode());
10         }
11     }
12 }
 1 /**
 2  *    測試類,測試使用枚舉數據類型實現單例
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    測試結果可以看出多個線程獲得的是同一個對象
 8      *        枚舉與靜態代碼塊相似,在使用枚舉類時,構造方法自動被調用
 9      */
10     public static void main(String[] args) {
11         MyThread t1 = new MyThread();
12         MyThread t2 = new MyThread();
13         MyThread t3 = new MyThread();
14         t1.start();
15         t2.start();
16         t3.start();
17     }
18 }

  完善使用enum枚舉數據類型實現單例模式:

 1 import java.sql.Connection;
 2 import java.sql.DriverManager;
 3 import java.sql.SQLException;
 4 
 5 /**
 6  *    使用enum枚舉類型實現單例模式
 7  */
 8 public class MyObject {
 9     public enum MyEnumSingletion{
10         connectionFactory;
11         
12         private Connection connection;
13         
14         private MyEnumSingletion() {
15             try {
16                 System.out.println("調用MyObject的構造方法");
17                 String url = "";
18                 String user = "";
19                 String password = "";
20                 String driverName = "";
21                 Class.forName(driverName);
22                 connection = DriverManager.getConnection(url, user, password);
23             } catch (ClassNotFoundException e) {
24                 e.printStackTrace();
25             } catch (SQLException e) {
26                 e.printStackTrace();
27             }
28         }
29         
30         public Connection getConnection() {
31             return connection;
32         }
33     }
34     
35     public static Connection getConnection() {
36         return MyEnumSingletion.connectionFactory.getConnection();
37     }
38 }
 1 /**
 2  *    線程類
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         for (int i = 0; i < 5; i++) {
 9             System.out.println(MyObject.getConnection().hashCode());
10         }
11     }
12 }
 1 /**
 2  *    測試類,測試使用枚舉數據類型實現單例
 3  */
 4 public class Run {
 5 
 6     public static void main(String[] args) {
 7         MyThread t1 = new MyThread();
 8         MyThread t2 = new MyThread();
 9         MyThread t3 = new MyThread();
10         t1.start();
11         t2.start();
12         t3.start();
13     }
14 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM