实现单例模式的多种方法

1.立即加载 -- “饿汉模式”

 立即加载就是使用类的时候已经将对象创建完毕。
  测试代码如下:
 1  public class Singleton {
 2   private static Singleton instance=new Singleton();
 3    private Singleton(){}
 4    public static Singleton getInstance(){
 5        /*
 6         * 此代码为立即加载,缺点是不能有其他实例变量
 7         * 因为getInstance()方法没有同步
 8         * 所以有可能出现非线程安全问题
 9         */
10        return instance;
11    }
12  }
13  
 1  public class Run {
 2   public static void main(String[] args) {
 3       MyThread t1=new MyThread();
 4       MyThread t2=new MyThread();
 5       MyThread t3=new MyThread();
 6     
 7       t1.start();
 8       t2.start();
 9       t3.start();
10   }
11 
12 }
1  public class MyThread extends Thread {
2   @Override
3   public void run() {
4       // TODO Auto-generated method stub
5       System.out.println(Singleton.getInstance().hashCode());
6   }
7  }
8  
以上的Run类和MyThread类在本文测试前四种方法一样,接下来就不在重复编码。
  运行结果如下:

2.延迟加载 -- “懒汉模式”

延迟加载就是在调用getXXX方法时,实例才会被创建。

2.1 普通“懒汉模式”

  测试代码如下:
 1 public class Singleton {
 2   private static Singleton instance;
 3    private Singleton(){}
 4    public static Singleton getInstance(){
 5      try {
 6        if (instance == null) {
 7         // 模拟在创建对象过程中有准备工作
 8         Thread.sleep(2000);
 9         instance = new Singleton();
10         }
11      } catch (InterruptedException e) {
12         // TODO Auto-generated catch block
13         e.printStackTrace();
14      }
15      return instance;
16    }
17 
18 }
此种方式,在多线程的环境下将无法实现单例。
运行结果如下:

2.2 线程安全下的“懒汉模式”

(1)使用synchronize关键字

测试代码如下:
 1 public class Singleton {
 2   private static Singleton instance;
 3 
 4   private Singleton() {
 5   }
 6 
 7    synchronized public static Singleton getInstance() {
 8      try {
 9         if (instance == null) {
10            // 模拟在创建对象过程中有准备工作
11            Thread.sleep(2000);
12            instance = new Singleton();
13          }
14        } catch (InterruptedException e) {
15             // TODO Auto-generated catch block
16             e.printStackTrace();
17        }
18       return instance;
19    }            
20 
21  }
22  

运行结果:

此方法虽然可以解决线程安全问题,但是对整个方法进行加锁无疑会使整个方法的运行效率大大降低。

(2)尝试同步代码块

测试代码如下:

 

 1 public class Singleton {
 2     private static Singleton instance;
 3 
 4     private Singleton() {
 5     }
 6 
 7     public static Singleton getInstance() {
 8          try {
 9                synchronized (Singleton.class) {
10 
11                     if (instance == null) {
12                          // 模拟在创建对象过程中有准备工作
13                         Thread.sleep(2000);
14                         instance = new Singleton();
15                     }
16                }
17           } catch (InterruptedException e) {
18                 // TODO Auto-generated catch block
19                e.printStackTrace();
20           }
21           return instance;
22     }
23 
24  }
25  
运行结果:

此方法虽然也是可以解决线程安全问题,但是它的运行效率和上一个方法的运行效率差不多,也是会大大拉低程序的效率

(3)针对某些重要的代码进行单独的同步

测试代码如下:
 1   public class Singleton {
 2     private static Singleton instance;
 3 
 4     private Singleton() {
 5     }
 6 
 7      public static Singleton getInstance() {
 8      try {
 9          if (instance == null) {
10            // 模拟在创建对象过程中有准备工作
11            Thread.sleep(2000);
12            synchronized (Singleton.class) {
13                instance = new Singleton();
14            }
15          }
16       } catch (InterruptedException e) {
17           // TODO Auto-generated catch block
18           e.printStackTrace();
19       }
20       return instance;
21      }
22 
23 }
24   

运行结果:

此方法虽然大幅度的解决了程序效率问题,但是却无法解决线程安全问题

(4)使用DCL双检查锁机制

测试代码如下:

 1 public class Singleton {
 2  private volatile static Singleton instance;
 3 
 4  private Singleton() {
 5  }
 6 
 7  public static Singleton getInstance() {
 8    try {
 9       if (instance == null) {
10       // 模拟在创建对象过程中有准备工作
11       Thread.sleep(2000);
12       synchronized (Singleton.class) {
13    if(instance==null)
14        instance = new Singleton();
15        }
16         }
17    } catch (InterruptedException e) {
18      // TODO Auto-generated catch block
19      e.printStackTrace();
20    }
21    return instance;
22   }
23 
24  }
25  
 1 public class Singleton {
 2   private volatile static Singleton instance;
 3 
 4   private Singleton() {
 5   }
 6 
 7   public static Singleton getInstance() {
 8      try {
 9         if (instance == null) {
10           // 模拟在创建对象过程中有准备工作
11           Thread.sleep(2000);
12           synchronized (Singleton.class) {
13            if(instance==null)
14                instance = new Singleton();
15            }
16         }
17      } catch (InterruptedException e) {
18          // TODO Auto-generated catch block
19          e.printStackTrace();
20      }
21      return instance;
22   }
23 
24 }
25  

运行结果如下:

此方法即完美解决了效率问题,也解决了线程安全问题,在懒汉模式下的最佳方法

3.使用静态内置类实现

测试代码如下:
 1 public class Singleton{
 2 
 3   private static class SigntonHandle{
 4     private static final Singleton instance=new Singleton();
 5   }
 6 
 7   private Singleton(){}
 8 
 9   public static Singleton getInstance(){
10       return SigntonHandle.instance;
11   }
12 
13  } 

运行结果:

 

由运行结果可知此方法可以实现单例模式,并且无线程安全问题,但是如果遇到序列化对象的时候,默认的运行方式就不是单例的

4.使用static块实现

 1 public class Singleton{
 2     private static Singleton instance=null;
 3         static{
 4         instance=new Singleton();
 5         }
 6     private Singleton(){}
 7     
 8     public static Singleton getInstance(){
 9         return instance;
10     }
11 }

运行结果:

 

由运行结果可知此方法可以实现单例模式,并且无线程安全问题。

5.使用序列化和反序列化实现

测试代码如下:

 1 public class Singleton implements Serializable{
 2     
 3     private static final long serialVersionUID = 1L;
 4 
 5     private static class SigntonHandle{
 6         private static final Singleton instance=new Singleton();
 7     }
 8     
 9     private Singleton(){}
10     
11     public static Singleton getInstance(){
12         return SigntonHandle.instance;
13     }
14     
15     protected Object readResolve(){
16         System.out.println("已調用readResolve方法");
17         return SigntonHandle.instance;
18     }
19 
20    }
 1 public class SaveAndRead {
 2     public static void main(String[] args) {
 3         try{
 4             Singleton instance=Singleton.getInstance();
 5             
 6             FileOutputStream fos=new FileOutputStream(new File("MyObject.txt"));
 7             ObjectOutputStream oos=new ObjectOutputStream(fos);
 8             oos.writeObject(instance);
 9             oos.close();
10             fos.close();
11             System.out.println(instance.hashCode());
12         }catch(Exception e){
13             e.printStackTrace();
14         }
15         try{
16             FileInputStream fis=new FileInputStream(new File("MyObject.txt"));
17             ObjectInputStream ois=new ObjectInputStream(fis);
18             Singleton instance1=(Singleton) ois.readObject();
19             ois.close();
20             fis.close();
21             System.out.println(instance1.hashCode());
22         }catch(Exception e){
23             e.printStackTrace();
24         }
25     }
26 
27    }

运行结果:

 

若注释调Singleton类中的readResolve方法,则运行结果如下:

 

由运行结果可知此方法可以实现单例模式,并且无线程安全问题,并且解决了在序列化对象中出现不是单例的情况。

6.使用enum枚举数据类型实现

此方法还含有MyThread类和Run类和之前的一致。
测试代码如下:

 1 public class Singleton{
 2     public enum EnumSingleton{
 3         singleton;
 4         private Singleton instance;
 5         private EnumSingleton(){
 6             instance=new Singleton();
 7         }
 8         public Singleton getInstance(){
 9             return instance;
10         }
11     }
12 
13     public static Singleton getInstance(){
14         return EnumSingleton.singleton.getInstance();
15     }
16 
17    }

运行结果:

由运行结果可知此方法可以实现单例模式,并且无线程安全问题。
posted on 2018-08-14 11:01  seizemiss  阅读(327)  评论(0编辑  收藏  举报