Home > Java > javaTutorial > Thread-safe singleton mode of java multi-threading

Thread-safe singleton mode of java multi-threading

高洛峰
Release: 2017-01-05 16:48:23
Original
1363 people have browsed it

Concept:
The singleton pattern is a common design pattern in Java. There are three types of singleton patterns: lazy-style singleton, hungry-style singleton, and registration-style singleton.
 The singleton pattern has the following characteristics:
 1. A 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, instantiates itself and provides this 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. These applications all have more or less the functionality of resource managers. Each computer can have several printers, but there can only be one Printer Spooler to prevent two print jobs from being output to the printer at the same time. Each computer can have several communication ports, and the system should centrally manage these communication ports to prevent one communication port from being called by two requests at the same time. In short, the purpose of choosing the singleton mode is to avoid inconsistent states and avoid long-term policies.

Here we mainly introduce two types in detail: lazy Chinese style and hungry Chinese style

1. Immediate loading/hungry Chinese style

The instance has been created before calling the method, code:

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;
  }
}
Copy after login

Create thread class

package com.weishiyao.learn.day8.singleton.ep1;
 
public class MyThread extends Thread {
  @Override
  public void run() {
    System.out.println(MyObject.getInstance().hashCode());
  }
}
Copy after login

Create Running 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();
  }
}
Copy after login

Running result
167772895
167772895
167772895
hashCode is the same value, indicating that the object is also the same, indicating that the immediate loading single-interest mode has been implemented

2. Delayed loading/lazy style

When calling the method The instance will be created later. The implementation plan can be to put the instantiation into the parameterless constructor, so that the instance of the object will be created only when called. Code:

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;
  }
}
Copy after login

Create 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());
  }
}
Copy after login

Create a running class

package com.weishiyao.learn.day8.singleton.ep2;
 
public class Run {
  public static void main(String[] args) {
    MyThread t1 = new MyThread();
    t1.start();
  }
}
Copy after login

Running results

167772895

Although an instance of an object is taken out, if it is in a multi-threaded environment, multiple instances will appear, so it is not a singleton mode

Run the 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();
  }
}
Copy after login

Running results

980258163
1224717057
1851889404
188820504
1672864109
Since there is a problem, we must solve it. Multi-threading solution in lazy mode, code:

The first solution, the most common, add synchronized , and synchronized can be added to different positions

The first one, method lock

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;
  }
}
Copy after login

This synchronized synchronization scheme leads to too low efficiency, 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() {
    // 延迟加载
    try {
      synchronized (MyObject.class) {
        if (myObject != null) {
           
        } else {
          // 模拟在创建对象之前做一些准备性的工作
          Thread.sleep(2000);
          myObject = new MyObject();
        }
      }
       
       
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return myObject;
  }
}
Copy after login

This The efficiency of the method is also very low. All the codes in the method are locked. Only the key codes need to be locked. The third synchronized usage plan
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;
  }
}
Copy after login

is written like this It seems to be the optimal solution, but after running the results, I found that it is actually non-thread-safe. Result:

1224717057

971173439

1851889404
1224717057
1672864109
Why?

Although the object creation statement is locked, only one thread can complete the creation at a time. However, when the first thread comes in to create the Object object, the second thread can still continue to create it, because we are tightly Only the creation statement is locked. The solution to this problem

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;
  }
}
Copy after login

only needs to add another judgment in the lock to ensure a 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

Main code

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;
  }
}
Copy after login

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

Running class

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

Result

1851889404

1851889404

1851889404
1851889404
1851889404
Through the internal static class, we get Thread-safe singleton mode

4. Serialization and deserialization singleton mode

Built-in static classes can achieve thread-safety issues, but if you encounter a serialized object, the result obtained by using the default method is still multiple instances. The

MyObject code

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;
//  }
}
Copy after login

Business class

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

The result

970928725

1099149023

Two different hashCode, proves that they are not the same object. The solution is to add the following code

protected MyObject readResolve() {
   System.out.println("调用了readResolve方法!");
   return MyObjectHandler.myObject;
 }
Copy after login

during deserialization Call, you can get the same object

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

The result


1255301379

The readResolve method was called!

1255301379
The same hashCode proves that the same object is obtained

5. Use static code blocks to implement singletons

The code in the static code block has already been executed when using the class, so you can apply the static code feature To realize the single profit mode

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;
  }
}
Copy after login

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

Running class

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

Running result:

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
通过静态代码块只执行一次的特性也成功的得到了线程安全的单例模式

六、使用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;
  }
}
Copy after login

线程类

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

运行类

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

运行结果

调用了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();
  }
}
Copy after login

更改线程代码

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

结果
调用了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

以上总结了单利模式与多线程结合时遇到的各种情况和解决方案,以供以后使用时查阅。

更多java多线程之线程安全的单例模式相关文章请关注PHP中文网!


source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template