欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時(shí)間:2016年03月28日 16:53:09   作者:寶寶快來  
這篇文章主要為大家詳細(xì)介紹了java多線程之線程安全的單例模式,文章內(nèi)容全面,感興趣的小伙伴們可以參考一下

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

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

一、立即加載/餓漢式

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

package com.weishiyao.learn.day8.singleton.ep1;

public class MyObject {
  // 立即加載方式==惡漢模式
  private static MyObject myObject = new MyObject();

  private MyObject() {
  }
  
  public static MyObject getInstance() {
    // 此代碼版本為立即加載
    // 此版本代碼的缺點(diǎn)是不能有其他實(shí)例變量
    // 因?yàn)間etInstance()方法沒有同步
    // 所以有可能出現(xiàn)非線程安全的問題
    return myObject;
  }
}

創(chuàng)建線程類

package com.weishiyao.learn.day8.singleton.ep1;

public class MyThread extends Thread {
  @Override
  public void run() {
    System.out.println(MyObject.getInstance().hashCode());
  }
}

創(chuàng)建運(yùn)行類

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();
  }
}

運(yùn)行結(jié)果
 167772895
 167772895
 167772895
hashCode是同一個(gè)值,說明對(duì)象也是同一個(gè),說明實(shí)現(xiàn)了立即加載型的單利模式

二、延遲加載/懶漢式

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

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)建線程類

package com.weishiyao.learn.day8.singleton.ep2;

public class MyThread extends Thread {
  @Override
  public void run() {
    System.out.println(MyObject.getInstance().hashCode());
  }
}

創(chuàng)建運(yùn)行類

package com.weishiyao.learn.day8.singleton.ep2;

public class Run {
  public static void main(String[] args) {
    MyThread t1 = new MyThread();
    t1.start();
  }
}

運(yùn)行結(jié)果

167772895

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

運(yùn)行測(cè)試類

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();
  }
}

運(yùn)行結(jié)果

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

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

第一種,方法鎖

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)建對(duì)象之前做一些準(zhǔn)備性的工作
        Thread.sleep(2000);
        myObject = new MyObject();
      }
      
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return myObject;
  }
}

這種synchronized的同步方案導(dǎo)致效率過于低下,整個(gè)方法都被鎖住

第二種synchronized使用方案

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)建對(duì)象之前做一些準(zhǔn)備性的工作
          Thread.sleep(2000);
          myObject = new MyObject();
        }
      }
      
      
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return myObject;
  }
}

這種方法效率一樣很低,方法內(nèi)的所有代碼都被鎖住,只需要鎖住關(guān)鍵代碼就好,第三種synchronized使用方案
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)建對(duì)象之前做一些準(zhǔn)備性的工作
          Thread.sleep(2000);
          synchronized (MyObject.class) {
            myObject = new MyObject();
          }
      }
      
      
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return myObject;
  }
}

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

結(jié)果:

1224717057
971173439
1851889404
1224717057
1672864109
Why?

雖然鎖住了對(duì)象創(chuàng)建的語句,每次只能有一個(gè)線程完成創(chuàng)建,但是,當(dāng)?shù)谝粋€(gè)線程進(jìn)來創(chuàng)建完成Object對(duì)象以后,第二個(gè)線程進(jìn)來還是可以繼續(xù)創(chuàng)建的,因?yàn)槲覀兙o緊只鎖住了創(chuàng)建語句,這個(gè)問題解決方案

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)建對(duì)象之前做一些準(zhǔn)備性的工作
          Thread.sleep(2000);
          synchronized (MyObject.class) {
            if (myObject == null) {
              myObject = new MyObject();
            }
          }
      }
      
      
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return myObject;
  }
}

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

結(jié)果如下:

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

主要代碼

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;
  }
}

線程類代碼

package com.weishiyao.learn.day8.singleton.ep4;

public class MyThread extends Thread {
  @Override
  public void run() {
    System.out.println(MyObject.getInstance().hashCode());
  }
}

運(yùn)行類

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();
  }
}

結(jié)果

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

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

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

MyObject代碼

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è)務(wù)類

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();
    }
    
    
  }
}

結(jié)果

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

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

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

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

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

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

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

MyObject類

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;
  }
}

線程類

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());
    }
  }
}

運(yùn)行類

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();
  }
}

運(yùn)行結(jié)果:

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ù)類型實(shí)現(xiàn)單例模式

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

MyObject類

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的構(gòu)造");
      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;
  }
}

線程類

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());
    }
  }
}

運(yùn)行類

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();
  }
}

運(yùn)行結(jié)果

調(diào)用了MyObject的構(gòu)造
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
上面這種寫法將枚舉類暴露了,違反了“職責(zé)單一原則”,可以使用一個(gè)類將枚舉包裹起來

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的構(gòu)造");
        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();
  }
}

更改線程代碼

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());
    }
  }
}

結(jié)果
調(diào)用了MyObject的構(gòu)造
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

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

相關(guān)文章

  • Java集合之Comparable和Comparator接口詳解

    Java集合之Comparable和Comparator接口詳解

    Java提供了Comparable接口與Comparator接口,它們?yōu)閿?shù)組或集合中的元素提供了排序邏輯,實(shí)現(xiàn)此接口的對(duì)象數(shù)組或集合可以通過Arrays.sort或Collections.sort進(jìn)行自動(dòng)排序。本文將通過示例講講它們的使用,需要的可以參考一下
    2022-12-12
  • SpringBoot報(bào)錯(cuò)Invalid?bound?statement?(not?found)問題排查和解決方案

    SpringBoot報(bào)錯(cuò)Invalid?bound?statement?(not?found)問題排查和解決方案

    這篇文章主要介紹了SpringBoot報(bào)錯(cuò)Invalid?bound?statement?(not?found)問題排查和解決方案,文中通過圖文結(jié)合的方式講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-03-03
  • Java中的Random和ThreadLocalRandom詳細(xì)解析

    Java中的Random和ThreadLocalRandom詳細(xì)解析

    這篇文章主要介紹了Java中的Random和ThreadLocalRandom詳細(xì)解析,Random 類用于生成偽隨機(jī)數(shù)的流, 該類使用48位種子,其使用線性同余公式進(jìn)行修改,需要的朋友可以參考下
    2024-01-01
  • SpringBoot淺析安全管理之OAuth2框架

    SpringBoot淺析安全管理之OAuth2框架

    安全管理是軟件系統(tǒng)必不可少的的功能。根據(jù)經(jīng)典的“墨菲定律”——凡是可能,總會(huì)發(fā)生。如果系統(tǒng)存在安全隱患,最終必然會(huì)出現(xiàn)問題,這篇文章主要介紹了SpringBoot安全管理OAuth2框架的使用
    2022-08-08
  • Spring數(shù)據(jù)庫連接池實(shí)現(xiàn)原理深入刨析

    Spring數(shù)據(jù)庫連接池實(shí)現(xiàn)原理深入刨析

    開發(fā)web項(xiàng)目,我們肯定會(huì)和數(shù)據(jù)庫打交道,因此就會(huì)涉及到數(shù)據(jù)庫鏈接的問題。在以前我們開發(fā)傳統(tǒng)的SSM結(jié)構(gòu)的項(xiàng)目時(shí)進(jìn)行數(shù)據(jù)庫鏈接都是通過JDBC進(jìn)行數(shù)據(jù)鏈接,我們每和數(shù)據(jù)庫打一次交道都需要先獲取一次鏈接,操作完后再關(guān)閉鏈接,這樣子效率很低,因此就出現(xiàn)了連接池
    2022-11-11
  • java RMI詳細(xì)介紹及實(shí)例講解

    java RMI詳細(xì)介紹及實(shí)例講解

    這篇文章主要介紹了java RMI詳細(xì)介紹及實(shí)例講解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • SpringBoot使用@Async注解可能會(huì)遇到的8大坑點(diǎn)匯總

    SpringBoot使用@Async注解可能會(huì)遇到的8大坑點(diǎn)匯總

    SpringBoot中,@Async注解可以實(shí)現(xiàn)異步線程調(diào)用,用法簡單,體驗(yàn)舒適,但是你一定碰到過異步調(diào)用不生效的情況,今天,我就列出90%的人都可能會(huì)遇到的8大坑點(diǎn),需要的朋友可以參考下
    2023-09-09
  • RestTemplat中關(guān)于getForobject方法的使用

    RestTemplat中關(guān)于getForobject方法的使用

    這篇文章主要介紹了RestTemplat中關(guān)于getForobject方法的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Spring Boot 文件上傳原理解析

    Spring Boot 文件上傳原理解析

    Spring Boot 文件上傳原理其實(shí)就是Spring MVC,因?yàn)檫@部分工作是Spring MVC做的而不是Spring Boot,那么,SpringMVC又是怎么處理文件上傳這個(gè)過程的呢?下面通過本文給大家詳細(xì)介紹下,一起看看吧
    2018-03-03
  • Tomcat+JDK安裝和配置教程

    Tomcat+JDK安裝和配置教程

    這篇文章主要為大家詳細(xì)介紹了Tomcat+JDK安裝和配置教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03

最新評(píng)論