激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - java多線程之線程安全的單例模式

java多線程之線程安全的單例模式

2020-04-13 11:42寶寶快來 JAVA教程

這篇文章主要為大家詳細介紹了java多線程之線程安全的單例模式,文章內(nèi)容全面,感興趣的小伙伴們可以參考一下

概念:
  java中單例模式是一種常見的設計模式,單例模式分三種:懶漢式單例、餓漢式單例、登記式單例三種。
  單例模式有一下特點:
  1、單例類只能有一個實例。
  2、單例類必須自己創(chuàng)建自己的唯一實例。
  3、單例類必須給所有其他對象提供這一實例。
  單例模式確保某個類只有一個實例,而且自行實例化并向整個系統(tǒng)提供這個實例。在計算機系統(tǒng)中,線程池、緩存、日志對象、對話框、打印機、顯卡的驅動程序對象常被設計成單例。這些應用都或多或少具有資源管理器的功能。每臺計算機可以有若干個打印機,但只能有一個Printer Spooler,以避免兩個打印作業(yè)同時輸出到打印機中。每臺計算機可以有若干通信端口,系統(tǒng)應當集中管理這些通信端口,以避免一個通信端口同時被兩個請求同時調(diào)用。總之,選擇單例模式就是為了避免不一致狀態(tài),避免政出多頭。

這里主要詳細介紹兩種:懶漢式和餓漢式

一、立即加載/餓漢式

在調(diào)用方法前,實例就已經(jīng)被創(chuàng)建,代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.weishiyao.learn.day8.singleton.ep1;
 
public class MyObject {
  // 立即加載方式==惡漢模式
  private static MyObject myObject = new MyObject();
 
  private MyObject() {
  }
  
  public static MyObject getInstance() {
    // 此代碼版本為立即加載
    // 此版本代碼的缺點是不能有其他實例變量
    // 因為getInstance()方法沒有同步
    // 所以有可能出現(xiàn)非線程安全的問題
    return myObject;
  }
}

創(chuàng)建線程類

?
1
2
3
4
5
6
7
8
package com.weishiyao.learn.day8.singleton.ep1;
 
public class MyThread extends Thread {
  @Override
  public void run() {
    System.out.println(MyObject.getInstance().hashCode());
  }
}

創(chuàng)建運行類

?
1
2
3
4
5
6
7
8
9
10
11
12
package com.weishiyao.learn.day8.singleton.ep1;
 
public class Run {
  public static void main(String[] args) {
    MyThread t1 = new MyThread();
    MyThread t2 = new MyThread();
    MyThread t3 = new MyThread();
    t1.start();
    t2.start();
    t3.start();
  }
}

運行結果
 167772895
 167772895
 167772895
hashCode是同一個值,說明對象也是同一個,說明實現(xiàn)了立即加載型的單利模式

二、延遲加載/懶漢式

在調(diào)用方法以后實例才會被創(chuàng)建,實現(xiàn)方案可以是將實例化放到無參構造函數(shù)當中,這樣只有當調(diào)用的時候才會創(chuàng)建對象的實例,代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.weishiyao.learn.day8.singleton.ep2;
 
public class MyObject {
  private static MyObject myObject;
  
  private MyObject() {
    
  }
  
  public static MyObject getInstance() {
    // 延遲加載
    if (myObject != null) {
      
    } else {
      myObject = new MyObject();
    }
    return myObject;
  }
}

創(chuàng)建線程類

?
1
2
3
4
5
6
7
8
package com.weishiyao.learn.day8.singleton.ep2;
 
public class MyThread extends Thread {
  @Override
  public void run() {
    System.out.println(MyObject.getInstance().hashCode());
  }
}

創(chuàng)建運行類

?
1
2
3
4
5
6
7
8
package com.weishiyao.learn.day8.singleton.ep2;
 
public class Run {
  public static void main(String[] args) {
    MyThread t1 = new MyThread();
    t1.start();
  }
}

運行結果

167772895

這樣雖然取出了一個對象的實例,但是如果在多線程的環(huán)境中,就會出現(xiàn)多個實例的情況,這樣就不是單例模式了

運行測試類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.weishiyao.learn.day8.singleton.ep2;
 
public class Run {
  public static void main(String[] args) {
    MyThread t1 = new MyThread();
    MyThread t2 = new MyThread();
    MyThread t3 = new MyThread();
    MyThread t4 = new MyThread();
    MyThread t5 = new MyThread();
    t1.start();
    t2.start();
    t3.start();
    t4.start();
    t5.start();
  }
}

運行結果

980258163
1224717057
1851889404
188820504
1672864109
既然出現(xiàn)問題,就要解決問題,在懶漢模式中的多線程的解決方案,代碼:

第一種方案,最常見的,加synchronized,而synchronized可以加到不同的位置

第一種,方法鎖

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.weishiyao.learn.day8.singleton.ep3;
 
public class MyObject {
  private static MyObject myObject;
  
  private MyObject() {
    
  }
  
  synchronized public static MyObject getInstance() {
    // 延遲加載
    try {
      if (myObject != null) {
        
      } else {
        // 模擬在創(chuàng)建對象之前做一些準備性的工作
        Thread.sleep(2000);
        myObject = new MyObject();
      }
      
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return myObject;
  }
}

這種synchronized的同步方案導致效率過于低下,整個方法都被鎖住

第二種synchronized使用方案

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.weishiyao.learn.day8.singleton.ep3;
 
public class MyObject {
  private static MyObject myObject;
  
  private MyObject() {
    
  }
  
  public static MyObject getInstance() {
    // 延遲加載
    try {
      synchronized (MyObject.class) {
        if (myObject != null) {
          
        } else {
          // 模擬在創(chuàng)建對象之前做一些準備性的工作
          Thread.sleep(2000);
          myObject = new MyObject();
        }
      }
      
      
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return myObject;
  }
}

這種方法效率一樣很低,方法內(nèi)的所有代碼都被鎖住,只需要鎖住關鍵代碼就好,第三種synchronized使用方案
package com.weishiyao.learn.day8.singleton.ep3;

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class MyObject {
  private static MyObject myObject;
  
  private MyObject() {
    
  }
  
  public static MyObject getInstance() {
    // 延遲加載
    try {
        if (myObject != null) {
          
        } else {
          // 模擬在創(chuàng)建對象之前做一些準備性的工作
          Thread.sleep(2000);
          synchronized (MyObject.class) {
            myObject = new MyObject();
          }
      }
      
      
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return myObject;
  }
}

這么寫看似是最優(yōu)方案了,但是,運行一下結果,發(fā)現(xiàn),其實它是非線程安全的

結果:

1224717057
971173439
1851889404
1224717057
1672864109
Why?

雖然鎖住了對象創(chuàng)建的語句,每次只能有一個線程完成創(chuàng)建,但是,當?shù)谝粋€線程進來創(chuàng)建完成Object對象以后,第二個線程進來還是可以繼續(xù)創(chuàng)建的,因為我們緊緊只鎖住了創(chuàng)建語句,這個問題解決方案

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.weishiyao.learn.day8.singleton.ep3;
 
public class MyObject {
  private static MyObject myObject;
  
  private MyObject() {
    
  }
  
  public static MyObject getInstance() {
    // 延遲加載
    try {
        if (myObject != null) {
          
        } else {
          // 模擬在創(chuàng)建對象之前做一些準備性的工作
          Thread.sleep(2000);
          synchronized (MyObject.class) {
            if (myObject == null) {
              myObject = new MyObject();
            }
          }
      }
      
      
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return myObject;
  }
}

只需要在鎖里面再添加一個判斷,就可以保證單例了,這個是DCL雙檢查機制

結果如下:

1224717057
1224717057
1224717057
1224717057
1224717057
 三、使用內(nèi)置靜態(tài)類實現(xiàn)單例

主要代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.weishiyao.learn.day8.singleton.ep4;
 
public class MyObject {
  // 內(nèi)部類方式
  private static class MyObjectHandler {
    private static MyObject myObject = new MyObject();
  }
 
  public MyObject() {
  }
  
  public static MyObject getInstance() {
    return MyObjectHandler.myObject;
  }
}

線程類代碼

?
1
2
3
4
5
6
7
8
package com.weishiyao.learn.day8.singleton.ep4;
 
public class MyThread extends Thread {
  @Override
  public void run() {
    System.out.println(MyObject.getInstance().hashCode());
  }
}

運行類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.weishiyao.learn.day8.singleton.ep4;
 
public class Run {
  public static void main(String[] args) {
    MyThread t1 = new MyThread();
    MyThread t2 = new MyThread();
    MyThread t3 = new MyThread();
    MyThread t4 = new MyThread();
    MyThread t5 = new MyThread();
    t1.start();
    t2.start();
    t3.start();
    t4.start();
    t5.start();
  }
}

結果

1851889404
1851889404
1851889404
1851889404
1851889404
通過內(nèi)部靜態(tài)類,得到了線程安全的單例模式

四、序列化和反序列化單例模式

內(nèi)置靜態(tài)類可以達到線程安全的問題,但如果遇到序列化對象時,使用默認方式得到的結果還是多例的

MyObject代碼

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.weishiyao.learn.day8.singleton.ep5;
 
import java.io.Serializable;
 
public class MyObject implements Serializable {
  
  /**
   *
   */
  private static final long serialVersionUID = 888L;
 
  // 內(nèi)部類方式
  private static class MyObjectHandler {
    private static MyObject myObject = new MyObject();
  }
 
  public MyObject() {
  }
  
  public static MyObject getInstance() {
    return MyObjectHandler.myObject;
  }
  
//  protected MyObject readResolve() {
//    System.out.println("調(diào)用了readResolve方法!");
//    return MyObjectHandler.myObject;
//  }
}

業(yè)務類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.weishiyao.learn.day8.singleton.ep5;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
 
public class SaveAndRead {
  public static void main(String[] args) {
    try {
      MyObject myObject = MyObject.getInstance();
      FileOutputStream fosRef = new FileOutputStream(new File("myObjectFile.txt"));
      ObjectOutputStream oosRef = new ObjectOutputStream(fosRef);
      oosRef.writeObject(myObject);
      oosRef.close();
      fosRef.close();
      System.out.println(myObject.hashCode());
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
    FileInputStream fisRef;
    try {
      fisRef = new FileInputStream(new File("myObjectFile.txt"));
      ObjectInputStream iosRef = new ObjectInputStream(fisRef);
      MyObject myObject = (MyObject) iosRef.readObject();
      iosRef.close();
      fisRef.close();
      System.out.println(myObject.hashCode());
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }
    
    
  }
}

結果

 970928725
 1099149023
兩個不同的hashCode,證明并不是同一個對象,解決方案,添加下面這段代碼

?
1
2
3
4
protected MyObject readResolve() {
   System.out.println("調(diào)用了readResolve方法!");
   return MyObjectHandler.myObject;
 }

在反序列化的時候調(diào)用,可以得到同一個對象

 System.out.println(myObject.readResolve().hashCode());
結果

 1255301379
 調(diào)用了readResolve方法!
 1255301379
相同的hashCode,證明得到了同一個對象

五、使用static代碼塊實現(xiàn)單例

靜態(tài)代碼塊中的代碼在使用類的時候就已經(jīng)執(zhí)行了,所以可以應用靜態(tài)代碼快這個特性來實現(xiàn)單利模式

MyObject類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.weishiyao.learn.day8.singleton.ep6;
 
public class MyObject {
  private static MyObject instance = null;
 
  private MyObject() {
    super();
  }
  
  static {
    instance = new MyObject();
  }
  
  public static MyObject getInstance() {
    return instance;
  }
}

線程類

?
1
2
3
4
5
6
7
8
9
10
package com.weishiyao.learn.day8.singleton.ep6;
 
public class MyThread extends Thread {
  @Override
  public void run() {
    for (int i = 0; i < 5; i++) {
      System.out.println(MyObject.getInstance().hashCode());
    }
  }
}

運行類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.weishiyao.learn.day8.singleton.ep6;
 
public class Run {
  public static void main(String[] args) {
    MyThread t1 = new MyThread();
    MyThread t2 = new MyThread();
    MyThread t3 = new MyThread();
    MyThread t4 = new MyThread();
    MyThread t5 = new MyThread();
    t1.start();
    t2.start();
    t3.start();
    t4.start();
    t5.start();
  }
}

運行結果:

1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
通過靜態(tài)代碼塊只執(zhí)行一次的特性也成功的得到了線程安全的單例模式

六、使用enum枚舉數(shù)據(jù)類型實現(xiàn)單例模式

枚舉enum和靜態(tài)代碼塊的特性類似,在使用枚舉時,構造方法會被自動調(diào)用,也可以用來實現(xiàn)單例模式

MyObject類

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.weishiyao.learn.day8.singleton.ep7;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
 
 
public enum MyObject {
  connectionFactory;
  
  private Connection connection;
  
  private MyObject() {
    try {
      System.out.println("調(diào)用了MyObject的構造");
      String url = "jdbc:mysql://172.16.221.19:3306/wechat_1?useUnicode=true&characterEncoding=UTF-8";
      String name = "root";
      String password = "111111";
      String driverName = "com.mysql.jdbc.Driver";
      Class.forName(driverName);
      connection = DriverManager.getConnection(url, name, password);
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    } catch (SQLException e) {
      e.printStackTrace();
    }
  }
  
  public Connection getConnection() {
    return connection;
  }
}

線程類

?
1
2
3
4
5
6
7
8
9
10
package com.weishiyao.learn.day8.singleton.ep7;
 
public class MyThread extends Thread {
  @Override
  public void run() {
    for (int i = 0; i < 5; i++) {
      System.out.println(MyObject.connectionFactory.getConnection().hashCode());
    }
  }
}

運行類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.weishiyao.learn.day8.singleton.ep7;
 
public class Run {
  public static void main(String[] args) {
    MyThread t1 = new MyThread();
    MyThread t2 = new MyThread();
    MyThread t3 = new MyThread();
    MyThread t4 = new MyThread();
    MyThread t5 = new MyThread();
    t1.start();
    t2.start();
    t3.start();
    t4.start();
    t5.start();
  }
}

運行結果

調(diào)用了MyObject的構造
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
上面這種寫法將枚舉類暴露了,違反了“職責單一原則”,可以使用一個類將枚舉包裹起來

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.weishiyao.learn.day8.singleton.ep8;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
 
 
public class MyObject {
  
  public enum MyEnumSingleton {
    connectionFactory;
    
    private Connection connection;
    
    private MyEnumSingleton() {
      try {
        System.out.println("調(diào)用了MyObject的構造");
        String url = "jdbc:mysql://172.16.221.19:3306/wechat_1?useUnicode=true&characterEncoding=UTF-8";
        String name = "root";
        String password = "111111";
        String driverName = "com.mysql.jdbc.Driver";
        Class.forName(driverName);
        connection = DriverManager.getConnection(url, name, password);
      } catch (ClassNotFoundException e) {
        e.printStackTrace();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    
    public Connection getConnection() {
      return connection;
    }
  }
  
  public static Connection getConnection() {
    return MyEnumSingleton.connectionFactory.getConnection();
  }
}

更改線程代碼

?
1
2
3
4
5
6
7
8
9
10
package com.weishiyao.learn.day8.singleton.ep8;
 
public class MyThread extends Thread {
  @Override
  public void run() {
    for (int i = 0; i < 5; i++) {
      System.out.println(MyObject.getConnection().hashCode());
    }
  }
}

結果
調(diào)用了MyObject的構造
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121

以上總結了單利模式與多線程結合時遇到的各種情況和解決方案,以供以后使用時查閱。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 黄色特级 | 高清做爰免费无遮网站挡 | 亚洲综合视频网站 | 国产高清美女一级毛片 | 中文字幕欧美一区二区三区 | 日本精品久久久久久草草 | 久久凹凸 | 成人国产高清 | 亚洲精品成人悠悠色影视 | 福利片在线看 | 手机在线看片国产 | 天天草天天爱 | 日本在线不卡一区二区 | 一级电影在线免费观看 | 一区二区三区欧美在线 | 黄色免费不卡视频 | 久久露脸国语精品国产91 | 免费国产网站 | 激情宗合 | 黄免费观看视频 | 午夜精品久久久久久久久久久久久蜜桃 | 国产一级伦理片 | 97干色 | 久久91精品国产91久久yfo | 免费观看欧美一级片 | 法国极品成人h版 | 日本在线一区二区 | 国产精品久久久免费 | 91短视频在线视频 | 19禁国产精品福利视频 | 国产伦精品一区二区三区 | 免费视频91 | 一级黄色欧美 | 草莓福利社区在线 | 九九视频在线观看6 | 久产久精品 | 欧美一级黄色免费 | 性大片性大片免费 | 久久久久久久一区二区三区 | 91社区电影 | 欧美日韩国产中文字幕 |