[b]单例模式[/b]是一个简单的创建者类型的模式,必须保证在系统中最多只能有一个唯一的对象实例,有如下特点:
[b]1、单例类必须保证最多只有一个实例;
2、必须由单例类自己创建唯一的实例;
3、单例类必须给其他所有对象提供其唯一的实例。[/b]
根据创建时间,单例模式又可以分为饥汉式和饱汉式两种,下面通过JAVA代码来分析二者的优缺点。
[b]饥汉式单例模式:[/b]
饥汉式单例模式优点是简单切线程安全,但是缺点也很明显,在类加载时就要初始化单例类的对象,有可能在整个系统的生命周期内都用不到该单例对象,这样就造成了浪费,特别是该单例类的生成和持有要占很大资源的时候。
饱汉式单例模式改掉了饥汉式单例模式的缺点,代码如下:
饱汉式单例模式虽然改进了饥汉式单例模式的缺点,但是每次在获取单例对象时都要锁(Class Lock),在单线程或没够对单例类对象有竞争的应用场景下,加锁会造成不必要的性能损失。
对于饱汉式单例模式的缺点,有人提出了所谓的“双重检查锁”的饱汉式单例模式,代码如下:
但是“双重检查锁”的饱汉式单例模式,实际上不是线程安全的:由于编译器的优化,允许出现主存和线程工作内存数据不一致问题,这就是“DCL失效”的问题,并不能保证这个双重检查锁定习语有效。它偶尔会失败,而不是总失败。具体原因请参详[url]http://www.ibm.com/developerworks/cn/java/j-dcl.html[/url]。
JAVA语言提供内部静态类,我们可以基于内部静态类的特性,改进饱汉式单例模式,提供一种高效而安全的单例模式,代码如下:
通过静态内部类的方式生成的单例模式,既保证了线程安全的,有保证了延迟加载。
在JDK发展到JDK 5.0之后,volatile + synchronized可以在保证内存模型的三个特性,即可见性、原子性和顺序性之外,对于被定义为volatile的变量,又禁用了volatile变量的重排序优化,就可以通过如下方式生成的单例模式,既保证安全性又兼顾延迟加载的功能,代码如下:
[b]1、单例类必须保证最多只有一个实例;
2、必须由单例类自己创建唯一的实例;
3、单例类必须给其他所有对象提供其唯一的实例。[/b]
根据创建时间,单例模式又可以分为饥汉式和饱汉式两种,下面通过JAVA代码来分析二者的优缺点。
[b]饥汉式单例模式:[/b]
/**
* HungerySingletonPatternTest
* 饥汉式单例模式
*/
public class HungerySingletonPatternTest{
private static final HungerySingletonPatternTest instance = new HungerySingletonPatternTest();
//构造函数声明为private,不能通过new关键字由外部生成实例对象
private HungerySingletonPatternTest(){
}
//只能通过该方法获取类的实例对象
public static HungerySingletonPatternTest getInstance(){
return instance;
}
}
饥汉式单例模式优点是简单切线程安全,但是缺点也很明显,在类加载时就要初始化单例类的对象,有可能在整个系统的生命周期内都用不到该单例对象,这样就造成了浪费,特别是该单例类的生成和持有要占很大资源的时候。
饱汉式单例模式改掉了饥汉式单例模式的缺点,代码如下:
/**
* FullSingletonPattern
* 饱汉式单例模式
*/
public class FullSingletonPatternTest{
private static final FullSingletonPatternTest instance = null;
//构造函数声明为private,不能通过new关键字由外部生成实例对象
private FullSingletonPatternTest(){
}
//只能通过该方法获取类的实例对象
public static synchronized FullSingletonPatternTest getInstance(){
if(instance == null){
new HungerySingletonPatternTest();
}
return instance;
}
}
饱汉式单例模式虽然改进了饥汉式单例模式的缺点,但是每次在获取单例对象时都要锁(Class Lock),在单线程或没够对单例类对象有竞争的应用场景下,加锁会造成不必要的性能损失。
对于饱汉式单例模式的缺点,有人提出了所谓的“双重检查锁”的饱汉式单例模式,代码如下:
/**
* DoubleCheckerSingletonPatternTest
* “双重检查锁”饱汉式单例模式
*/
public class DoubleCheckerSingletonPatternTest{
private static final DoubleCheckerPatternTest instance = null;
//构造函数声明为private,不能通过new关键字由外部生成实例对象
private DoubleCheckerSingletonPatternTest(){
}
//只能通过该方法获取类的实例对象
public static DoubleCheckerSingletonPatternTest getInstance(){
if(instance == null){
synchronized(DoubleCheckerSingletonPatternTest.class){
if(instance == null){
new DoubleCheckerSingletonPatternTest();
}
}
}
return instance;
}
}
但是“双重检查锁”的饱汉式单例模式,实际上不是线程安全的:由于编译器的优化,允许出现主存和线程工作内存数据不一致问题,这就是“DCL失效”的问题,并不能保证这个双重检查锁定习语有效。它偶尔会失败,而不是总失败。具体原因请参详[url]http://www.ibm.com/developerworks/cn/java/j-dcl.html[/url]。
JAVA语言提供内部静态类,我们可以基于内部静态类的特性,改进饱汉式单例模式,提供一种高效而安全的单例模式,代码如下:
/**
* StaticInnerClassSingletonPatternTest
* 基于内部静态类的单例模式
*/
public class StaticInnerClassSingletonPatternTest{
private StaticInnerClassSingletonPatternTest{}
//定义一个私有的内部静态类并在其中初始化外部单例对象
private static class SingletonHolder{
static final StaticInnerClassSingletonPatternTest INSTANCE = new StaticInnerClassSingletonPatternTest();
}
//只能通过该方法获取类的实例对象
public static StaticInnerClassSingletonPatternTest getInstance(){
return StaticInnerClassSingletonPatternTest.INSTANCE;
}
}
通过静态内部类的方式生成的单例模式,既保证了线程安全的,有保证了延迟加载。
在JDK发展到JDK 5.0之后,volatile + synchronized可以在保证内存模型的三个特性,即可见性、原子性和顺序性之外,对于被定义为volatile的变量,又禁用了volatile变量的重排序优化,就可以通过如下方式生成的单例模式,既保证安全性又兼顾延迟加载的功能,代码如下:
/**
* NewDoubleCheckerSingletonPatternTest
* volatile + synchronized的“双重检查锁”饱汉式单例模式
*/
public class NewDoubleCheckerSingletonPatternTest{
private static volatile NewDoubleCheckerSingletonPatternTest instance = null;
//构造函数声明为private,不能通过new关键字由外部生成实例对象
private NewDoubleCheckerSingletonPatternTest(){
}
//只能通过该方法获取类的实例对象
public static NewDoubleCheckerSingletonPatternTest getInstance(){
if(instance == null){
synchronized(NewDoubleCheckerSingletonPatternTest.class){
if(instance == null){
new DoubleCheckerSingletonPatternTest();
}
}
}
return instance;
}
}