程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> java多線程之線程平安的單例形式

java多線程之線程平安的單例形式

編輯:關於JAVA

java多線程之線程平安的單例形式。本站提示廣大學習愛好者:(java多線程之線程平安的單例形式)文章只能為提供參考,不一定能成為您想要的結果。以下是java多線程之線程平安的單例形式正文


概念:
  java中單例形式是一種罕見的設計形式,單例形式分三種:懶漢式單例、餓漢式單例、掛號式單例三種。
  單例形式有一下特色:
  1、單例類只能有一個實例。
  2、單例類必需本身創立本身的獨一實例。
  3、單例類必需給一切其他對象供給這一實例。
  單例形式確保某個類只要一個實例,並且自行實例化並向全部體系供給這個實例。在盤算機體系中,線程池、緩存、日記對象、對話框、打印機、顯卡的驅動法式對象常被設計成單例。這些運用都或多或少具有資本治理器的功效。每台盤算機可以有若干個打印機,但只能有一個Printer Spooler,以免兩個打印功課同時輸入到打印機中。每台盤算機可以有若干通訊端口,體系應該集中治理這些通訊端口,以免一個通訊端口同時被兩個要求同時挪用。總之,選擇單例形式就是為了不紛歧致狀況,防止政出多頭。

這裡重要具體引見兩種:懶漢式和餓漢式

1、立刻加載/餓漢式

在挪用辦法前,實例就曾經被創立,代碼:

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

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

  private MyObject() {
  }
  
  public static MyObject getInstance() {
    // 此代碼版本為立刻加載
    // 此版本代碼的缺陷是不克不及有其他實例變量
    // 由於getInstance()辦法沒有同步
    // 所以有能夠湧現非線程平安的成績
    return myObject;
  }
}

創立線程類

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

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

創立運轉類

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是統一個值,解釋對象也是統一個,解釋完成了立刻加載型的單利形式

2、延遲加載/懶漢式

在挪用辦法今後實例才會被創立,完成計劃可所以將實例化放到無參結構函數傍邊,如許只要當挪用的時刻才會創立對象的實例,代碼:

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

創立線程類

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

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

創立運轉類

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

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

運轉成果

167772895

如許固然掏出了一個對象的實例,然則假如在多線程的情況中,就會湧現多個實例的情形,如許就不是單例形式了

運轉測試類

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
既然湧現成績,就要處理成績,在懶漢形式中的多線程的處理計劃,代碼:

第一種計劃,最多見的,加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 {
        // 模仿在創立對象之前做一些預備性的任務
        Thread.sleep(2000);
        myObject = new MyObject();
      }
      
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return myObject;
  }
}

這類synchronized的同步計劃招致效力過於低下,全部辦法都被鎖住

第二種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 {
          // 模仿在創立對象之前做一些預備性的任務
          Thread.sleep(2000);
          myObject = new MyObject();
        }
      }
      
      
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return myObject;
  }
}

這類辦法效力一樣很低,辦法內的一切代碼都被鎖住,只須要鎖住症結代碼就好,第三種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 {
          // 模仿在創立對象之前做一些預備性的任務
          Thread.sleep(2000);
          synchronized (MyObject.class) {
            myObject = new MyObject();
          }
      }
      
      
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return myObject;
  }
}

這麼寫看似是最優計劃了,然則,運轉一下成果,發明,其實它長短線程平安的

成果:

1224717057
971173439
1851889404
1224717057
1672864109
Why?

固然鎖住了對象創立的語句,每次只能有一個線程完成創立,然則,當第一個線程出去創立完成Object對象今後,第二個線程出去照樣可以持續創立的,由於我們牢牢只鎖住了創立語句,這個成績處理計劃

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 {
          // 模仿在創立對象之前做一些預備性的任務
          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
 3、應用內置靜態類完成單例

重要代碼

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

public class MyObject {
  // 外部類方法
  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());
  }
}

運轉類

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
經由過程外部靜態類,獲得了線程平安的單例形式

4、序列化和反序列化單例形式

內置靜態類可以到達線程平安的成績,但假如碰到序列化對象時,應用默許方法獲得的成果照樣多例的

MyObject代碼

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

import java.io.Serializable;

public class MyObject implements Serializable {
  
  /**
   * 
   */
  private static final long serialVersionUID = 888L;

  // 外部類方法
  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("挪用了readResolve辦法!");
//    return MyObjectHandler.myObject;
//  }
}

營業類

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,證實其實不是統一個對象,處理計劃,添加上面這段代碼

 protected MyObject readResolve() {
    System.out.println("挪用了readResolve辦法!");
    return MyObjectHandler.myObject;
  }

在反序列化的時刻挪用,可以獲得統一個對象

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

 1255301379
 挪用了readResolve辦法!
 1255301379
雷同的hashCode,證實獲得了統一個對象

5、應用static代碼塊完成單例

靜態代碼塊中的代碼在應用類的時刻就曾經履行了,所以可以運用靜態代碼快這個特征來完成單利形式

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

運轉類

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
經由過程靜態代碼塊只履行一次的特征同樣成功的獲得了線程平安的單例形式

6、應用enum列舉數據類型完成單例形式

列舉enum和靜態代碼塊的特征相似,在應用列舉時,結構辦法會被主動挪用,也能夠用來完成單例形式

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("挪用了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;
  }
}

線程類

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

運轉類

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

運轉成果

挪用了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
下面這類寫法將列舉類裸露了,違背了“職責單一准繩”,可使用一個類將列舉包裹起來

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("挪用了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();
  }
}

更改線程代碼

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

成果
挪用了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

以上總結了單利形式與多線程聯合時碰到的各類情形息爭決計劃,以供今後應用時查閱。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved