Thread safe singleton mode for java multithreading

  • 2020-05-07 19:41:30
  • OfStack

concept:
The singleton pattern in
java is a common design pattern. There are three types of singleton patterns: lazy singleton, hungry singleton and registered singleton.
The singleton pattern has the following characteristics:
1. The singleton class can only have one instance.
2. The singleton class must create its own unique instance.
3. The singleton class must provide this instance to all other objects.
The singleton pattern ensures that a class has only one instance and that it instantiates itself and provides that instance to the entire system. In computer systems, thread pools, caches, log objects, dialog boxes, printers, and graphics card driver objects are often designed as singletons. All of these applications are more or less a resource manager. Each computer can have several printers, but only one Printer Spooler, to avoid having two print jobs output to the printer at the same time. Each computer can have a number of communication ports, and the system should centrally manage these communication ports so that one communication port is not simultaneously invoked by two requests. In short, the choice of singleton mode is to avoid the state of non - 1, to avoid the government out of long.

Here mainly introduces two kinds in detail: lazy Chinese style and hungry Chinese style

1. Load now/hunger-style

The instance is created before the method is called. The code:


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

public class MyObject {
  //  Immediate loading mode == Villian mode 
  private static MyObject myObject = new MyObject();

  private MyObject() {
  }
  
  public static MyObject getInstance() {
    //  This code version is loaded immediately 
    //  The disadvantage of this version of the code is that there can be no other instance variables 
    //  because getInstance() Method is not synchronized 
    //  So it's possible to have a non-thread-safe problem 
    return myObject;
  }
}

Creating a thread class


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

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

Create a run class


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

The results
  167772895
  167772895
  167772895
hashCode is the same value, indicating that the object is also the same, indicating that the immediate loading type of simple interest mode is implemented

2. Lazy loading/lazy

The instance will not be created until the method is called. The implementation scheme can be to put the instantiation into the no-argument constructor, so that the instance of the object will be created only when the method is called. The code:


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

public class MyObject {
  private static MyObject myObject;
  
  private MyObject() {
    
  }
  
  public static MyObject getInstance() {
    //  Lazy loading 
    if (myObject != null) {
      
    } else {
      myObject = new MyObject();
    }
    return myObject;
  }
}

Creating a thread class


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

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

Create a run class


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

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

The results

167772895

This takes out an instance of an object, but in a multi-threaded environment, there are multiple instances, so it's not a singleton

Run test class


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

The results

980258163
1224717057
1851889404
188820504
1672864109
Since there is a problem, to solve the problem, in lazy mode multi-threaded solution, code:

1, the most common, plus synchronized, and synchronized can be added to different locations

First, method locking


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

public class MyObject {
  private static MyObject myObject;
  
  private MyObject() {
    
  }
  
  synchronized public static MyObject getInstance() {
    //  Lazy loading 
    try {
      if (myObject != null) {
        
      } else {
        //  The simulation is done before the object is created 1 Some preparatory work 
        Thread.sleep(2000);
        myObject = new MyObject();
      }
      
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return myObject;
  }
}

This synchronized synchronization scheme results in too much inefficiency, and the entire method is locked

The second synchronized usage scheme


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

public class MyObject {
  private static MyObject myObject;
  
  private MyObject() {
    
  }
  
  public static MyObject getInstance() {
    //  Lazy loading 
    try {
      synchronized (MyObject.class) {
        if (myObject != null) {
          
        } else {
          //  The simulation is done before the object is created 1 Some preparatory work 
          Thread.sleep(2000);
          myObject = new MyObject();
        }
      }
      
      
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return myObject;
  }
}

This method is very inefficient. All the code in the method is locked. Only the key code needs to be locked
package com.weishiyao.learn.day8.singleton.ep3;


public class MyObject {
  private static MyObject myObject;
  
  private MyObject() {
    
  }
  
  public static MyObject getInstance() {
    //  Lazy loading 
    try {
        if (myObject != null) {
          
        } else {
          //  The simulation is done before the object is created 1 Some preparatory work 
          Thread.sleep(2000);
          synchronized (MyObject.class) {
            myObject = new MyObject();
          }
      }
      
      
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return myObject;
  }
}

This looks like an optimal solution, but after running 1, it turns out to be non-thread-safe

Results:

1224717057
971173439
1851889404
1224717057
1672864109
Why?

Although the statement of object creation is locked, only one thread can complete the creation at a time. However, after the first thread comes in to complete the Object object, the second thread can continue to create the Object object


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

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

0

You only need to add one more judgment to the lock to ensure the singleton. This is the DCL double check mechanism

The results are as follows:

1224717057
1224717057
1224717057
1224717057
1224717057
  3. Use the built-in static class to implement the singleton

The main code


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

public class MyObject {
  //  Inner class mode 
  private static class MyObjectHandler {
    private static MyObject myObject = new MyObject();
  }

  public MyObject() {
  }
  
  public static MyObject getInstance() {
    return MyObjectHandler.myObject;
  }
}

Thread class code


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

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

Run the class


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

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

3

The results of

1851889404
1851889404
1851889404
1851889404
1851889404
The thread-safe singleton pattern is obtained through the internal static class

4. Serialization and deserialization singleton pattern

The built-in static classes can achieve thread-safety, but if you run into a serialized object, the result is multiple

MyObject code


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

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

4

Business class


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

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

5

The results of

  970928725
  1099149023
Two different hashCode, proving that they are not the same object solution, add the following code


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

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

6

Called during deserialization, you get the same object

  System.out.println(myObject.readResolve().hashCode());
results

  1255301379
  calls the readResolve method!
  1255301379
Same hashCode, same object

5. Use the static code block to realize the singleton

The code in the static code block is already executing when the class is used, so you can use the static code fast feature to implement the single-interest pattern

MyObject class


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

Thread class


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

Run the class


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

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

9

Operation results:

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
The thread-safe singleton mode is also successfully obtained by using the feature that static code blocks are executed only once

6. Use enum enumerated data types to implement the singleton pattern

Enumeration of enum is similar to static code blocks in that the constructor is called automatically when enumeration is used and can also be used to implement the singleton pattern

MyObject class


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(" Call the MyObject The structure of the ");
      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;
  }
}

Thread class


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

Run the class


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

The results

The MyObject construct is called
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
The above notation exposes the enumeration class as a violation of the "duty sheet 1" principle, and one class can be used to wrap the enumeration


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(" Call the MyObject The structure of the ");
        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();
  }
}

Changing thread code


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

The results of
The MyObject construct is called
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

The above summarizes the various situations and solutions encountered when the single-interest mode is combined with multi-threading for future reference.


Related articles: