单例模式-多线程环境

2018-09-05 07:48:19来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

单例模式-多线程环境

 

  单例-立即加载:

 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 }

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:java中利用dom4j解析XML文件

下一篇:Netty 系列七(那些开箱即用的 ChannelHandler).