• 单例模式-多线程环境


    单例模式-多线程环境

      单例-立即加载:

     1 /**
     2  *    单例模式,立即加载
     3  */
     4 public class MyObject {
     5     
     6     private static MyObject myObject = new MyObject();//立即加载(类加载的时候就已经实例化好了)
     7     
     8     private MyObject() {}
     9     
    10     public static MyObject getInstance() {
    11         /**
    12          *     此代码版本为立即加载
    13          *     缺点是不能有其他实例变量,因为getInstance()方法没有同步,所有可能出现非线程安全问题
    14          */
    15         return myObject;
    16     }
    17 }
     1 /**
     2  *    线程类
     3  */
     4 public class MyThread extends Thread {
     5     
     6     @Override
     7     public void run() {
     8         System.out.println(MyObject.getInstance().hashCode());
     9     }
    10 }
     1 /**
     2  *    测试类,创建三个线程分别去获取实例并输出hashCode值
     3  *        通过结果可以看出三个线程打印的hashCode值相同,说明对象是同一个,并且实现了立即加载型单例设计模式
     4  */
     5 public class Run {
     6 
     7     public static void main(String[] args) {
     8         MyThread t1 = new MyThread();
     9         MyThread t2 = new MyThread();
    10         MyThread t3 = new MyThread();
    11 
    12         t1.start();
    13         t2.start();
    14         t3.start();
    15     }
    16 }

      单例-延迟加载:(该版本单例模式,如果在多线程环境,则可能会出现多个实例)

     1 /**
     2  *    单例设计类,延迟加载,当需要用再创建实例
     3  */
     4 public class MyObject {
     5     
     6     private static MyObject myObject;
     7     
     8     private MyObject() {}
     9     
    10     public static MyObject getInstance() {
    11         if(myObject != null) {//延迟加载,也就是当用到了再创建
    12         }else {
    13             myObject = new MyObject();
    14         }
    15         return myObject;
    16     }
    17 }
     1 /**
     2  *    线程类
     3  */
     4 public class MyThread extends Thread {
     5     
     6     @Override
     7     public void run() {
     8         System.out.println(MyObject.getInstance().hashCode());
     9     }
    10 }
     1 /**
     2  *    测试类
     3  */
     4 public class Run {
     5 
     6     /**
     7      *    如果是在多线程环境,会出现多个实例的情况,此时就与单例模式的初衷相违背了
     8      */
     9     public static void main(String[] args) {
    10         MyThread t1 = new MyThread();
    11         t1.start();
    12     }
    13 }

      演示:延迟单例模式,出现多个实例

     1 /**
     2  *    延迟加载单例模式类
     3  */
     4 public class MyObject {
     5     
     6     private static MyObject myObject;
     7     
     8     private MyObject() {}
     9     
    10     public static MyObject getInstance() {
    11         try {
    12             if(myObject != null) {
    13             }else {
    14                 Thread.sleep(3000);//模拟在创建对象之前做一些准备性工作
    15                 myObject = new MyObject();
    16             }
    17         } catch (InterruptedException e) {
    18             e.printStackTrace();
    19         }
    20         return myObject;
    21     }
    22 }
     1 /**
     2  *    线程类
     3  */
     4 public class MyThread extends Thread {
     5     
     6     @Override
     7     public void run() {
     8         System.out.println(MyObject.getInstance().hashCode());
     9     }
    10 }
     1 /**
     2  *    测试类
     3  */
     4 public class Run {
     5 
     6     /**
     7      *    通过运行结果可以看到打印的三个线程获取的对象的hashCode值不同,也就是出现了多例
     8      */
     9     public static void main(String[] args) {
    10         MyThread t1 = new MyThread();
    11         MyThread t2 = new MyThread();
    12         MyThread t3 = new MyThread();
    13         t1.start();
    14         t2.start();
    15         t3.start();
    16     }
    17 }

      优化1:优化出现多例的情况,将整个方法加上同步锁

     1 /**
     2  *    延迟加载单例模式类
     3  */
     4 public class MyObject {
     5     
     6     private static MyObject myObject;
     7     
     8     private MyObject() {}
     9     
    10     //防止多线程出现多例,此处将整个方法加上同步锁(效率低)
    11     synchronized public static MyObject getInstance() {
    12         try {
    13             if(myObject != null) {
    14             }else {
    15                 Thread.sleep(3000);//模拟在创建对象之前做一些准备性工作
    16                 myObject = new MyObject();
    17             }
    18         } catch (InterruptedException e) {
    19             e.printStackTrace();
    20         }
    21         return myObject;
    22     }
    23 }
     1 /**
     2  *    线程类
     3  */
     4 public class MyThread extends Thread {
     5     
     6     @Override
     7     public void run() {
     8         System.out.println(MyObject.getInstance().hashCode());
     9     }
    10 }
     1 /**
     2  *    测试类
     3  */
     4 public class Run {
     5 
     6     /**
     7      *    测试结果可以看出是同步运行,但是整个方法加上同步锁,效率太低
     8      */
     9     public static void main(String[] args) {
    10         MyThread t1 = new MyThread();
    11         MyThread t2 = new MyThread();
    12         MyThread t3 = new MyThread();
    13         t1.start();
    14         t2.start();
    15         t3.start();
    16     }
    17 }

      再次优化:使用同步代码块

     1 /**
     2  *    延迟加载单例模式类
     3  */
     4 public class MyObject {
     5     
     6     private static MyObject myObject;
     7     
     8     private MyObject() {}
     9     
    10     //防止多线程出现多例,在使用同步代码块
    11     public static MyObject getInstance() {
    12         try {
    13             synchronized(MyObject.class) {
    14                 if(myObject != null) {
    15                 }else {
    16                     Thread.sleep(3000);//模拟在创建对象之前做一些准备性工作
    17                     myObject = new MyObject();
    18                 }
    19             }
    20         } catch (InterruptedException e) {
    21             e.printStackTrace();
    22         }
    23         return myObject;
    24     }
    25 }
     1 /**
     2  *    线程类
     3  */
     4 public class MyThread extends Thread {
     5     
     6     @Override
     7     public void run() {
     8         System.out.println(MyObject.getInstance().hashCode());
     9     }
    10 }
     1 /**
     2  *    测试类
     3  */
     4 public class Run {
     5 
     6     /**
     7      *    测试结果可以看出是同步运行,修改成使用同步代码块,效率也是低因为整个方法的代码都在同步块中
     8      */
     9     public static void main(String[] args) {
    10         MyThread t1 = new MyThread();
    11         MyThread t2 = new MyThread();
    12         MyThread t3 = new MyThread();
    13         t1.start();
    14         t2.start();
    15         t3.start();
    16     }
    17 }

      继续优化:针对重要代码使用同步代码块

     1 /**
     2  *    延迟加载单例模式类
     3  */
     4 public class MyObject {
     5     
     6     private static MyObject myObject;
     7     
     8     private MyObject() {}
     9     
    10     public static MyObject getInstance() {
    11         try {
    12             if(myObject != null) {
    13             }else {
    14                 Thread.sleep(3000);//模拟在创建对象之前做一些准备性工作
    15                 //虽然部分代码上锁,但是还是存在非线程安全问题
    16                 synchronized(MyObject.class) {
    17                     myObject = new MyObject();
    18                 }
    19             }
    20         } catch (InterruptedException e) {
    21             e.printStackTrace();
    22         }
    23         return myObject;
    24     }
    25 }
     1 /**
     2  *    线程类
     3  */
     4 public class MyThread extends Thread {
     5     
     6     @Override
     7     public void run() {
     8         System.out.println(MyObject.getInstance().hashCode());
     9     }
    10 }
     1 /**
     2  *    测试类
     3  */
     4 public class Run {
     5 
     6     /**
     7      *    测试结果可以看出得到多个实例
     8      *        该优化只对实例对象的关键代码进行同步,结构上来说效率提升了,但是遇到多线程,还是无法解决得到同一个实例对象
     9      */
    10     public static void main(String[] args) {
    11         MyThread t1 = new MyThread();
    12         MyThread t2 = new MyThread();
    13         MyThread t3 = new MyThread();
    14         t1.start();
    15         t2.start();
    16         t3.start();
    17     }
    18 }

      终极优化:使用DCL双检查锁机制实现多线程中延迟加载单例设计模式,解决出现多例的情况以及效率问题

     1 /**
     2  *    延迟加载单例模式类
     3  */
     4 public class MyObject {
     5     //使用volatile关键字
     6     private volatile static MyObject myObject;
     7     
     8     private MyObject() {}
     9     
    10     public static MyObject getInstance() {
    11         try {
    12             if(myObject != null) {
    13             }else {
    14                 Thread.sleep(3000);//模拟在创建对象之前做一些准备性工作
    15                 synchronized(MyObject.class) {
    16                     if(myObject == null) {
    17                         myObject = new MyObject();
    18                     }
    19                 }
    20             }
    21         } catch (InterruptedException e) {
    22             e.printStackTrace();
    23         }
    24         return myObject;
    25     }
    26 }
     1 /**
     2  *    线程类
     3  */
     4 public class MyThread extends Thread {
     5     
     6     @Override
     7     public void run() {
     8         System.out.println(MyObject.getInstance().hashCode());
     9     }
    10 }
     1 /**
     2  *    测试类
     3  */
     4 public class Run {
     5 
     6     /**
     7      *    测试结果可以看出多个线程获得的是同一个对象
     8      *        使用双重检查锁功能,解决懒汉模式遇到的多线程问题
     9      */
    10     public static void main(String[] args) {
    11         MyThread t1 = new MyThread();
    12         MyThread t2 = new MyThread();
    13         MyThread t3 = new MyThread();
    14         t1.start();
    15         t2.start();
    16         t3.start();
    17     }
    18 }

      使用静态内部类实现单例:

     1 /**
     2  *    延迟加载单例模式类
     3  */
     4 public class MyObject {
     5     
     6     //静态内部类
     7     public static class MyObjectHandler{
     8         private static MyObject myObject = new MyObject();
     9     }
    10     
    11     private MyObject() {}
    12     
    13     public static MyObject getInstance() {
    14         return MyObjectHandler.myObject;
    15     }
    16 }
     1 /**
     2  *    线程类
     3  */
     4 public class MyThread extends Thread {
     5     
     6     @Override
     7     public void run() {
     8         System.out.println(MyObject.getInstance().hashCode());
     9     }
    10 }
     1 /**
     2  *    测试类
     3  */
     4 public class Run {
     5 
     6     /**
     7      *    测试结果可以看出多个线程获得的是同一个对象
     8      */
     9     public static void main(String[] args) {
    10         MyThread t1 = new MyThread();
    11         MyThread t2 = new MyThread();
    12         MyThread t3 = new MyThread();
    13         t1.start();
    14         t2.start();
    15         t3.start();
    16     }
    17 }

      序列化与反序列化的单例设计模式:

     1 import java.io.Serializable;
     2 
     3 /**
     4  *    延迟加载单例模式类
     5  */
     6 public class MyObject implements Serializable{
     7     
     8     private static final long serialVersionUID = 1L;
     9 
    10     //静态内部类
    11     public static class MyObjectHandler{
    12         private static MyObject myObject = new MyObject();
    13     }
    14     
    15     private MyObject() {}
    16     
    17     public static MyObject getInstance() {
    18         return MyObjectHandler.myObject;
    19     }
    20     //保证反序列化拿到的对象与序列化对象是同一个对象
    21     protected Object readResolve() {
    22         System.out.println("调用了readResolve()方法");
    23         return MyObjectHandler.myObject;
    24     }
    25 }
     1 import java.io.File;
     2 import java.io.FileInputStream;
     3 import java.io.FileNotFoundException;
     4 import java.io.FileOutputStream;
     5 import java.io.IOException;
     6 import java.io.ObjectInputStream;
     7 import java.io.ObjectOutputStream;
     8 
     9 /**
    10  *    测试类
    11  */
    12 public class Run {
    13     public static void main(String[] args) {
    14         //将对象序列化到文件
    15         try {
    16             MyObject myObject = MyObject.getInstance();
    17             FileOutputStream fops = new FileOutputStream(new File("myObjectFile.text"));
    18             ObjectOutputStream oops = new ObjectOutputStream(fops);
    19             oops.writeObject(myObject);
    20             oops.close();
    21             fops.close();
    22             System.out.println(myObject.hashCode());
    23         } catch (FileNotFoundException e) {
    24             e.printStackTrace();
    25         } catch (IOException e) {
    26             e.printStackTrace();
    27         }
    28         //将文件中序列化的对象反序列化并输出到控制台
    29         try {
    30             FileInputStream fips = new FileInputStream(new File("myObjectFile.text"));
    31             ObjectInputStream oips = new ObjectInputStream(fips);
    32             MyObject readObject = (MyObject)oips.readObject();
    33             oips.close();
    34             fips.close();
    35             System.out.println(readObject.hashCode());
    36         } catch (FileNotFoundException e) {
    37             e.printStackTrace();
    38         } catch (IOException e) {
    39             e.printStackTrace();
    40         } catch (ClassNotFoundException e) {
    41             e.printStackTrace();
    42         }
    43     }
    44 }

      使用静态代码块实现单例:

     1 /**
     2  *    延迟加载单例模式类
     3  */
     4 public class MyObject {
     5     
     6     private static MyObject instance = null;
     7     
     8     private MyObject() {}
     9     
    10     static {
    11         instance = new MyObject();
    12     }
    13     
    14     public static MyObject getInstance() {
    15         return instance;
    16     }
    17 }
     1 /**
     2  *    线程类
     3  */
     4 public class MyThread extends Thread {
     5     
     6     @Override
     7     public void run() {
     8         for (int i = 0; i < 5; i++) {
     9             System.out.println(MyObject.getInstance().hashCode());
    10         }
    11     }
    12 }
     1 /**
     2  *    测试类,测试使用静态代码块实现单例
     3  */
     4 public class Run {
     5 
     6     /**
     7      *    测试结果可以看出多个线程获得的是同一个对象
     8      */
     9     public static void main(String[] args) {
    10         MyThread t1 = new MyThread();
    11         MyThread t2 = new MyThread();
    12         MyThread t3 = new MyThread();
    13         t1.start();
    14         t2.start();
    15         t3.start();
    16     }
    17 }

      使用enum枚举数据类型实现单例模式:

     1 import java.sql.Connection;
     2 import java.sql.DriverManager;
     3 import java.sql.SQLException;
     4 
     5 /**
     6  *    使用enum枚举类型实现单例模式,此处的枚举类进行了暴露,违反了职责单一原则
     7  */
     8 public enum MyObject {
     9     
    10     connectionFactory;
    11     
    12     private Connection connection;
    13     
    14     private MyObject() {
    15         try {
    16             System.out.println("调用MyObject的构造方法");
    17             String url = "";
    18             String user = "";
    19             String password = "";
    20             String driverName = "";
    21             Class.forName(driverName);
    22             connection = DriverManager.getConnection(url, user, password);
    23         } catch (ClassNotFoundException e) {
    24             e.printStackTrace();
    25         } catch (SQLException e) {
    26             e.printStackTrace();
    27         }
    28     }
    29     
    30     public Connection getConnection() {
    31         return connection;
    32     }
    33 }
     1 /**
     2  *    线程类
     3  */
     4 public class MyThread extends Thread {
     5     
     6     @Override
     7     public void run() {
     8         for (int i = 0; i < 5; i++) {
     9             System.out.println(MyObject.connectionFactory.getConnection().hashCode());
    10         }
    11     }
    12 }
     1 /**
     2  *    测试类,测试使用枚举数据类型实现单例
     3  */
     4 public class Run {
     5 
     6     /**
     7      *    测试结果可以看出多个线程获得的是同一个对象
     8      *        枚举与静态代码块相似,在使用枚举类时,构造方法自动被调用
     9      */
    10     public static void main(String[] args) {
    11         MyThread t1 = new MyThread();
    12         MyThread t2 = new MyThread();
    13         MyThread t3 = new MyThread();
    14         t1.start();
    15         t2.start();
    16         t3.start();
    17     }
    18 }

      完善使用enum枚举数据类型实现单例模式:

     1 import java.sql.Connection;
     2 import java.sql.DriverManager;
     3 import java.sql.SQLException;
     4 
     5 /**
     6  *    使用enum枚举类型实现单例模式
     7  */
     8 public class MyObject {
     9     public enum MyEnumSingletion{
    10         connectionFactory;
    11         
    12         private Connection connection;
    13         
    14         private MyEnumSingletion() {
    15             try {
    16                 System.out.println("调用MyObject的构造方法");
    17                 String url = "";
    18                 String user = "";
    19                 String password = "";
    20                 String driverName = "";
    21                 Class.forName(driverName);
    22                 connection = DriverManager.getConnection(url, user, password);
    23             } catch (ClassNotFoundException e) {
    24                 e.printStackTrace();
    25             } catch (SQLException e) {
    26                 e.printStackTrace();
    27             }
    28         }
    29         
    30         public Connection getConnection() {
    31             return connection;
    32         }
    33     }
    34     
    35     public static Connection getConnection() {
    36         return MyEnumSingletion.connectionFactory.getConnection();
    37     }
    38 }
     1 /**
     2  *    线程类
     3  */
     4 public class MyThread extends Thread {
     5     
     6     @Override
     7     public void run() {
     8         for (int i = 0; i < 5; i++) {
     9             System.out.println(MyObject.getConnection().hashCode());
    10         }
    11     }
    12 }
     1 /**
     2  *    测试类,测试使用枚举数据类型实现单例
     3  */
     4 public class Run {
     5 
     6     public static void main(String[] args) {
     7         MyThread t1 = new MyThread();
     8         MyThread t2 = new MyThread();
     9         MyThread t3 = new MyThread();
    10         t1.start();
    11         t2.start();
    12         t3.start();
    13     }
    14 }
  • 相关阅读:
    SSAS维度成员中的非法XML字符[转]
    Java 的sun.jdbc.odbc桥连接的使用!
    SSAS全半角空格导致的AS处理错误[转]
    【原创】C# 如何自定义代码模版
    TO_DATE函数
    JDK Windows环境变量配置
    SSAS异常字符导致找不到属性键错误[转]
    SQL SERVER 数据库日期算法总结
    章节 2.1 可靠的软件 – 灵活,可靠的软件 使用设计模式和敏捷开发
    软件架构设计和Scrum潜在可交付产品,我(scrum master)和他(架构师)的讨论
  • 原文地址:https://www.cnblogs.com/wang1001/p/9584670.html
Copyright © 2020-2023  润新知