[color=green][size=medium][b]Java之设计模式之Singleton[/b][/size][/color]
Java Singleton 单例设计模式属于四大设计模式之生产设计模式的一种。
该设计模式看似简单,但是涉及到许多注意点。
[size=medium][b]一、Java Singleton 简介[/b][/size]
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
等
[size=medium][b]二、实现 Java Singleton [/b][/size]
实现单例需要符合下列三点:
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 单例)
下面将逐一讲解:
[b]1、Eager initializtion (急初始化)[/b]
急初始化模式下,单例在 Class 被 JVM 加载时即被创建。
优点:
-容易实现。
缺点:
-该类未被使用到时也被初始化。
-不能进行异常处理。
点评:
如果使用较少的资源,可以使用此方法。
多数情形下,如 File System,Database Connection,应避免使用此方法。
代码:
[b]2、Static block initializtion (静态块初始化)[/b]
静态块初始化方法跟急初始化差不多,只是可以在静态块中处理异常。
[b]3、Lazy initializtion (懒初始化)[/b]
懒初始化单例设计模式,是在对外的 public 方法中生成实例对象的。
只需加一个是否为空的判断即可。
上面3种单例设计方式,在单线程的环境下可以正常工作。
但是,在多线程的环境下不行:当多个线程同时运行在 if 块内,
可能会产生多个实例对象。
下面讲述多线程环境下的单例模式。
[b]4、Thread Safe Singleton(线程安全式单例)[/b]
最简单的实现是给对外公开的 public 的方法增加 synchronized 修饰符。
说明:[url=http://lixh1986.iteye.com/blog/2351243]关于 synchronized修饰符 [/url]
这样一次只能有一个线程可以访问该方法。
上面的方法在功能实现角度上是没有任何问题的。但是有一缺陷:效率比较低。
其实,只需对实例第一次产生时进行 synchroinized 即可。
改进:
[b]5、Bill Push Singleton(Bill Push 单例)[/b]
在 Java 1.5 之前,java 内存模型有很多问题。
尤其是线程很多的情况下,上面的实现方式会莫名其妙的失效。
于是乎,Bill Push 提出了单例模式的另一种实现方式:借助内部类。
内部类何时被初始化?
当类被 JVM 装载时,内部类不会被装载,直到 getInstance() 方法被调用。
这是被最广泛使用的线程安全的单例模式。
因为它不需要任何的 synchronized 修饰符。
[size=medium][b]三、Java Singleton 之继续讨论[/b][/size]
[b]1、Using Reflection to destroy Signleton Pattern[/b]
使用反射来破坏单例设计模式:
当使用反射机制生成类的实例时,上述单例设计方法便失效了。
上述两个 hashCode() 值是不同的。
[b]2、Enum Singleton(枚举式单例)[/b]
为了克服使用反射所带来的问题, Joshua Bloch 建议使用 Enum 实现单例。
因为 Java 可以保证 enum value 只会被初始化一次。
既于 enum 的值都是 public 的,这种方式可以作为单例的实现之一。
缺点:
不够灵活。
急初始化,不能懒初始化。
[b]3、Serializtion and Singleton(单例序列化)[/b]
在分布式系统中,有时候需要对单例进行序列化(实现 Serializable 接口),
以便在系统磁盘中(或网络传输中)保存单例的状态,然后在后续的某个点取得具状态的单例。
在反序列化单例时,可能会使得到的实例与原来的不同(如果单例仅仅实现 Serializable 接口)。
解决方案是重写 Serializtion API 中的 readResolve() 方法。
测试类:
-
转载请注明,
原文出处:http://lixh1986.iteye.com/blog/2351353
引用:
http://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples
-
Java Singleton 单例设计模式属于四大设计模式之生产设计模式的一种。
该设计模式看似简单,但是涉及到许多注意点。
[size=medium][b]一、Java Singleton 简介[/b][/size]
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
等
[size=medium][b]二、实现 Java Singleton [/b][/size]
实现单例需要符合下列三点:
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 单例)
下面将逐一讲解:
[b]1、Eager initializtion (急初始化)[/b]
急初始化模式下,单例在 Class 被 JVM 加载时即被创建。
优点:
-容易实现。
缺点:
-该类未被使用到时也被初始化。
-不能进行异常处理。
点评:
如果使用较少的资源,可以使用此方法。
多数情形下,如 File System,Database Connection,应避免使用此方法。
代码:
public class EagerInitializedSingleton {
private static final EagerInitializedSingleton instance
= new EagerInitializedSingleton();
//私有构造方法,避免其它类在该类外部构造此类的实例。
private EagerInitializedSingleton(){}
public static EagerInitializedSingleton getInstance(){
return instance;
}
}
[b]2、Static block initializtion (静态块初始化)[/b]
静态块初始化方法跟急初始化差不多,只是可以在静态块中处理异常。
public class StaticBlockSingleton {
private static StaticBlockSingleton instance;
private StaticBlockSingleton(){}
//static block initialization for exception handling
static{
try{
instance = new StaticBlockSingleton();
}catch(Exception e){
throw new RuntimeException("Exception occured in creating singleton instance");
}
}
public static StaticBlockSingleton getInstance(){
return instance;
}
}
[b]3、Lazy initializtion (懒初始化)[/b]
懒初始化单例设计模式,是在对外的 public 方法中生成实例对象的。
只需加一个是否为空的判断即可。
public class LazyInitializedSingleton {
private static LazyInitializedSingleton instance;
private LazyInitializedSingleton(){}
public static LazyInitializedSingleton getInstance(){
if(instance == null){
instance = new LazyInitializedSingleton();
}
return instance;
}
}
上面3种单例设计方式,在单线程的环境下可以正常工作。
但是,在多线程的环境下不行:当多个线程同时运行在 if 块内,
可能会产生多个实例对象。
下面讲述多线程环境下的单例模式。
[b]4、Thread Safe Singleton(线程安全式单例)[/b]
最简单的实现是给对外公开的 public 的方法增加 synchronized 修饰符。
说明:[url=http://lixh1986.iteye.com/blog/2351243]关于 synchronized修饰符 [/url]
这样一次只能有一个线程可以访问该方法。
public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;
private ThreadSafeSingleton(){}
public static synchronized ThreadSafeSingleton getInstance(){
if(instance == null){
instance = new ThreadSafeSingleton();
}
return instance;
}
}
上面的方法在功能实现角度上是没有任何问题的。但是有一缺陷:效率比较低。
其实,只需对实例第一次产生时进行 synchroinized 即可。
改进:
public class ThreadSafeDoubleCheckingSingleton {
private static ThreadSafeDoubleCheckingSingleton instance;
private ThreadSafeDoubleCheckingSingleton(){}
public static ThreadSafeDoubleCheckingSingleton getInstance(){
if(instance == null){
synchronized(ThreadSafeDoubleCheckingSingleton.class){
if(instance == null){
instance = new ThreadSafeDoubleCheckingSingleton();
}
}
}
return instance;
}
}
[b]5、Bill Push Singleton(Bill Push 单例)[/b]
在 Java 1.5 之前,java 内存模型有很多问题。
尤其是线程很多的情况下,上面的实现方式会莫名其妙的失效。
于是乎,Bill Push 提出了单例模式的另一种实现方式:借助内部类。
public class BillPughSingleton {
private BillPughSingleton(){}
private static class SingletonHelper{
private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}
public static BillPughSingleton getInstance(){
return SingletonHelper.INSTANCE;
}
}
内部类何时被初始化?
当类被 JVM 装载时,内部类不会被装载,直到 getInstance() 方法被调用。
这是被最广泛使用的线程安全的单例模式。
因为它不需要任何的 synchronized 修饰符。
[size=medium][b]三、Java Singleton 之继续讨论[/b][/size]
[b]1、Using Reflection to destroy Signleton Pattern[/b]
使用反射来破坏单例设计模式:
当使用反射机制生成类的实例时,上述单例设计方法便失效了。
public class ReflectionSingletonTest {
public static void main(String[] args) throws Exception{
EagerInitializedSingleton instanceOne =
EagerInitializedSingleton.getInstance();
EagerInitializedSingleton instanceTwo = null;
Constructor constructor = EagerInitializedSingleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
instanceTwo = (EagerInitializedSingleton) constructor.newInstance();
System.out.println(instanceOne.hashCode());
System.out.println(instanceTwo.hashCode());
}
}
上述两个 hashCode() 值是不同的。
[b]2、Enum Singleton(枚举式单例)[/b]
为了克服使用反射所带来的问题, Joshua Bloch 建议使用 Enum 实现单例。
因为 Java 可以保证 enum value 只会被初始化一次。
既于 enum 的值都是 public 的,这种方式可以作为单例的实现之一。
缺点:
不够灵活。
急初始化,不能懒初始化。
public enum EnumSingleton {
INSTANCE;
public static void doSomething(){
System.out.println("do something...");
}
}
[b]3、Serializtion and Singleton(单例序列化)[/b]
在分布式系统中,有时候需要对单例进行序列化(实现 Serializable 接口),
以便在系统磁盘中(或网络传输中)保存单例的状态,然后在后续的某个点取得具状态的单例。
在反序列化单例时,可能会使得到的实例与原来的不同(如果单例仅仅实现 Serializable 接口)。
解决方案是重写 Serializtion API 中的 readResolve() 方法。
import java.io.Serializable;
public class SerializedSingleton implements Serializable{
private static final long serialVersionUID = -7604766932017737115L;
private SerializedSingleton(){}
private static class SingletonHelper{
private static final SerializedSingleton instance = new SerializedSingleton();
}
public static SerializedSingleton getInstance(){
return SingletonHelper.instance;
}
// This will fix the de-serialization issue
private Object readResolve() {
// Return the available instance instead.
return getInstance();
}
}
/*
readResolve() is used for replacing the object read from the stream.
The only use I've ever seen for this is enforcing singletons;
when an object is read, replace it with the singleton instance.
This ensures that nobody can create another instance by serializing
and de-serializing the singleton.
*/
测试类:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
public class SerializedSingletonTest {
public static void main(String[] args)
throws FileNotFoundException, IOException, ClassNotFoundException {
SerializedSingleton instanceOne = SerializedSingleton.getInstance();
ObjectOutput out = new ObjectOutputStream(new FileOutputStream("filename.ser"));
out.writeObject(instanceOne);
out.close();
//deserailize from file to object
ObjectInput in = new ObjectInputStream(new FileInputStream("filename.ser"));
SerializedSingleton instanceTwo = (SerializedSingleton) in.readObject();
in.close();
System.out.println("instanceOne hashCode = "+instanceOne.hashCode());
System.out.println("instanceTwo hashCode = "+instanceTwo.hashCode());
}
}
-
转载请注明,
原文出处:http://lixh1986.iteye.com/blog/2351353
引用:
http://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples
-