Java之设计模式之Singleton

Java之设计模式之Singleton  

Java Singleton 单例设计模式属于四大设计模式之生产设计模式的一种。 
该设计模式看似简单,但是涉及到许多注意点。 
伦理片 http://www.dotdy .com/  
一、Java Singleton 简介  

Java Singleton 单例设计只允许在JVM中产生类的一个实例对象。 
因此这样的类不能提供对外的构造方法产生实例,而是提供一个 public 方法, 
返回(指向)某个实例的引用。 

Java Singleton 单例设计常用于: 
1、Logging(日志) 
2、Driver(驱动) 
3、Caching(缓存) 
4、Thread Pool(线程池) 
5、Abstract Factory(抽象工厂) 
6、Builder(构建器) 
7、Prototype(原型设计) 
8、Facade Design Pattern 

在 core java 中也有出现单例模式的设计: 
java.lang.Runtime 
java.awt.Desktop 
等 

二、实现 Java Singleton 

实现单例需要符合下列三点: 
1、private 的构造方法。阻止除本类外的其他类对该类进行实例的构建。 
2、private , static 的属性。这就是那个单例。 
3、public static 的方法,用于对外返回单例。 


单例的实现有以下几种方式: 

按初始化分: 
1、Eager initializtion (急初始化) 
2、Static block initializtion (静态块初始化) 
3、Lazy initializtion (懒初始化) 

按线程安全分: 
4、Thread Safe Singleton(线程安全式单例) 
5、Bill Push Singleton(Bill Push 单例) 


下面将逐一讲解: 

1、Eager initializtion (急初始化)  

急初始化模式下,单例在 Class 被 JVM 加载时即被创建。 

优点: 
    -容易实现。 

缺点: 
    -该类未被使用到时也被初始化。 
    -不能进行异常处理。 

点评: 
如果使用较少的资源,可以使用此方法。 
多数情形下,如 File System,Database Connection,应避免使用此方法。 

代码: 
Java代码   收藏代码
  1. public class EagerInitializedSingleton {  
  2.       
  3.     private static final EagerInitializedSingleton instance   
  4.                              = new EagerInitializedSingleton();  
  5.       
  6.     //私有构造方法,避免其它类在该类外部构造此类的实例。  
  7.     private EagerInitializedSingleton(){}  
  8.   
  9.     public static EagerInitializedSingleton getInstance(){  
  10.         return instance;  
  11.     }  
  12. }  



2、Static block initializtion (静态块初始化)  

静态块初始化方法跟急初始化差不多,只是可以在静态块中处理异常。 
Java代码   收藏代码
  1. public class StaticBlockSingleton {  
  2.   
  3.     private static StaticBlockSingleton instance;  
  4.       
  5.     private StaticBlockSingleton(){}  
  6.       
  7.     //static block initialization for exception handling  
  8.     static{  
  9.         try{  
  10.             instance = new StaticBlockSingleton();  
  11.         }catch(Exception e){  
  12.             throw new RuntimeException("Exception occured in creating singleton instance");  
  13.         }  
  14.     }  
  15.       
  16.     public static StaticBlockSingleton getInstance(){  
  17.         return instance;  
  18.     }  
  19. }  


3、Lazy initializtion (懒初始化)  

懒初始化单例设计模式,是在对外的 public 方法中生成实例对象的。 
只需加一个是否为空的判断即可。 

Java代码   收藏代码
  1. public class LazyInitializedSingleton {  
  2.   
  3.     private static LazyInitializedSingleton instance;  
  4.       
  5.     private LazyInitializedSingleton(){}  
  6.       
  7.     public static LazyInitializedSingleton getInstance(){  
  8.         if(instance == null){  
  9.             instance = new LazyInitializedSingleton();  
  10.         }  
  11.         return instance;  
  12.     }  
  13. }  

上面3种单例设计方式,在单线程的环境下可以正常工作。 
但是,在多线程的环境下不行:当多个线程同时运行在 if 块内, 
可能会产生多个实例对象。 

下面讲述多线程环境下的单例模式。 

4、Thread Safe Singleton(线程安全式单例)  

最简单的实现是给对外公开的 public 的方法增加 synchronized 修饰符。 

说明: 关于 synchronized修饰符 

这样一次只能有一个线程可以访问该方法。 
Java代码   收藏代码
  1. public class ThreadSafeSingleton {  
  2.   
  3.     private static ThreadSafeSingleton instance;  
  4.       
  5.     private ThreadSafeSingleton(){}  
  6.       
  7.     public static synchronized ThreadSafeSingleton getInstance(){  
  8.         if(instance == null){  
  9.             instance = new ThreadSafeSingleton();  
  10.         }  
  11.         return instance;  
  12.     }  
  13.       
  14. }  

上面的方法在功能实现角度上是没有任何问题的。但是有一缺陷:效率比较低。 
其实,只需对实例第一次产生时进行 synchroinized 即可。 

改进: 
Java代码   收藏代码
  1. public class ThreadSafeDoubleCheckingSingleton {  
  2.   
  3.     private static ThreadSafeDoubleCheckingSingleton instance;  
  4.       
  5.     private ThreadSafeDoubleCheckingSingleton(){}  
  6.       
  7.     public static ThreadSafeDoubleCheckingSingleton getInstance(){  
  8.         if(instance == null){  
  9.             synchronized(ThreadSafeDoubleCheckingSingleton.class){  
  10.                 if(instance == null){  
  11.                     instance = new ThreadSafeDoubleCheckingSingleton();  
  12.                 }  
  13.             }  
  14.         }  
  15.         return instance;  
  16.     }  
  17.       
  18. }  


5、Bill Push Singleton(Bill Push 单例)  

在 Java 1.5 之前,java 内存模型有很多问题。 
尤其是线程很多的情况下,上面的实现方式会莫名其妙的失效。 

于是乎,Bill Push 提出了单例模式的另一种实现方式:借助内部类。 
Java代码   收藏代码
  1. public class BillPughSingleton {  
  2.   
  3.     private BillPughSingleton(){}  
  4.       
  5.     private static class SingletonHelper{  
  6.         private static final BillPughSingleton INSTANCE = new BillPughSingleton();  
  7.     }  
  8.       
  9.     public static BillPughSingleton getInstance(){  
  10.         return SingletonHelper.INSTANCE;  
  11.     }  
  12. }  

内部类何时被初始化? 
当类被 JVM 装载时,内部类不会被装载,直到 getInstance() 方法被调用。 


这是被最广泛使用的线程安全的单例模式。 
因为它不需要任何的 synchronized 修饰符。 


三、Java Singleton 之继续讨论  

1、Using Reflection to destroy Signleton Pattern  

使用反射来破坏单例设计模式: 
当使用反射机制生成类的实例时,上述单例设计方法便失效了。 
Java代码   收藏代码
  1. public class ReflectionSingletonTest {  
  2.   
  3.     public static void main(String[] args) throws Exception{  
  4.         EagerInitializedSingleton instanceOne =   
  5.                                    EagerInitializedSingleton.getInstance();          
  6.         EagerInitializedSingleton instanceTwo = null;  
  7.           
  8.         Constructor constructor = EagerInitializedSingleton.class.getDeclaredConstructor();  
  9.         constructor.setAccessible(true);  
  10.         instanceTwo = (EagerInitializedSingleton) constructor.newInstance();  
  11.           
  12.         System.out.println(instanceOne.hashCode());  
  13.         System.out.println(instanceTwo.hashCode());  
  14.     }  
  15. }  

上述两个 hashCode() 值是不同的。 


2、Enum Singleton(枚举式单例)  

为了克服使用反射所带来的问题, Joshua Bloch 建议使用 Enum 实现单例。 
因为 Java 可以保证 enum value 只会被初始化一次。 
既于 enum 的值都是 public 的,这种方式可以作为单例的实现之一。 

缺点: 
不够灵活。 
急初始化,不能懒初始化。 
Java代码   收藏代码
  1. public enum EnumSingleton {  
  2.   
  3.     INSTANCE;  
  4.       
  5.     public static void doSomething(){  
  6.         System.out.println("do something...");  
  7.     }  
  8. }  


3、Serializtion and Singleton(单例序列化)  

在分布式系统中,有时候需要对单例进行序列化(实现 Serializable 接口), 
以便在系统磁盘中(或网络传输中)保存单例的状态,然后在后续的某个点取得具状态的单例。 

在反序列化单例时,可能会使得到的实例与原来的不同(如果单例仅仅实现 Serializable 接口)。 
解决方案是重写 Serializtion API 中的 readResolve() 方法。 

Java代码   收藏代码
  1. import java.io.Serializable;  
  2.   
  3. public class SerializedSingleton implements Serializable{  
  4.   
  5.     private static final long serialVersionUID = -7604766932017737115L;  
  6.   
  7.     private SerializedSingleton(){}  
  8.       
  9.     private static class SingletonHelper{  
  10.         private static final SerializedSingleton instance = new SerializedSingleton();  
  11.     }  
  12.       
  13.     public static SerializedSingleton getInstance(){  
  14.         return SingletonHelper.instance;  
  15.     }  
  16.       
  17.     // This will fix the de-serialization issue  
  18.     private Object readResolve() {  
  19.          // Return the available instance instead.  
  20.         return getInstance();     
  21.     }  
  22.       
  23. }  
  24.   
  25. /* 
  26.      
  27.     readResolve() is used for replacing the object read from the stream.  
  28.     The only use I've ever seen for this is enforcing singletons;  
  29.     when an object is read, replace it with the singleton instance.  
  30.     This ensures that nobody can create another instance by serializing  
  31.     and de-serializing the singleton. 
  32.  
  33. */  


测试类: 

Java代码   收藏代码
  1. import java.io.FileInputStream;  
  2. import java.io.FileNotFoundException;  
  3. import java.io.FileOutputStream;  
  4. import java.io.IOException;  
  5. import java.io.ObjectInput;  
  6. import java.io.ObjectInputStream;  
  7. import java.io.ObjectOutput;  
  8. import java.io.ObjectOutputStream;  
  9.   
  10. public class SerializedSingletonTest {  
  11.   
  12.     public static void main(String[] args)   
  13.             throws FileNotFoundException, IOException, ClassNotFoundException {  
  14.         SerializedSingleton instanceOne = SerializedSingleton.getInstance();  
  15.         ObjectOutput out = new ObjectOutputStream(new FileOutputStream("filename.ser"));  
  16.         out.writeObject(instanceOne);  
  17.         out.close();  
  18.           
  19.         //deserailize from file to object  
  20.         ObjectInput in = new ObjectInputStream(new FileInputStream("filename.ser"));  
  21.         SerializedSingleton instanceTwo = (SerializedSingleton) in.readObject();  
  22.         in.close();  
  23.           
  24.         System.out.println("instanceOne hashCode = "+instanceOne.hashCode());  
  25.         System.out.println("instanceTwo hashCode = "+instanceTwo.hashCode());  
  26.           
  27.     }  
  28.   
  29. }  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值