您的位置:首页 > 其它

单例模式

2018-02-28 16:03 211 查看

单例模式定义

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

单例模式优缺点

优点

在内存中只有一个实例,节约内存,减少性能开销。当一个对象的产生需要占用较多资源时(如文件、读取配置文件等),采用单例模式可有效节约资源。

单例对象可以作为全局访问点,优化和共享全局资源访问。

缺点

单例模式要求自行实例化,因此单例模式一般没有接口,拓展性不好。

单例模式使用场景

要求生成唯一序列号的场景

在整个项目中需要一个共享访问点或共享资源

创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源

需要定义大量的静态变量和静态方法的场景

单例模式代码形式

饿汉形式

public class Singleton{
private static Singleton instance = new Singleton();//在类加载时即进行创建
private Singleton(){}
public static Singleton newInstance(){
return instance;
}
}


饿汉形式中该对象在类加载的时候就完成了对象的创建,不存在线程安全问题。当对象占用内存小而且初始化后很快就会被用到时,饿汉形式很适用;但是如果这个对象并不急着用或者根本没有用到,该对象还是会被创建,如果该对象占用的内存空间较大,则造成了系统资源的浪费。

懒汉形式

public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static Singleton newInstance(){
if(instance == null){
instance =  new Singleton();
}
return instance;
}
}


懒汉形式中该对象时在需要的时候才去创建,即按需使用,在单例使用次数少、占用资源多的场景下比较适用。但是懒汉形式并不是线程安全的,当有多个线程同时调用newInstance方法时,可能会创建多个实例。

线程安全的懒汉形式

public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static synchronized Singleton newInstance(){//使用synchronized关键字同步
if(instance == null){
instance =  new Singleton();
}
return instance;
}
}


这种形式下,线程每次调用newInstance方法都会先获取对象锁,然后再执行方法体,在高并发情况下,对象锁有可能升级为重量级锁,带来一定的性能问题。

一种看起来可行的改进方式–双重检查锁定形式

既然存在性能问题,我们将代码稍加改进:

public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static Singleton newInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance =  new Singleton();
}
}
}
return instance;
}
}


在这种形式的代码下,只要对象被线程安全的创建成功,将不会在存在性能问题。然而既然是“看起来可行的”办法,那么一定有什么隐患。

可靠的改进方式–volatile

上面的双重检查锁定形式不可靠的地方在于它并不能保证线程安全,根本原因在于
instance = new Singleton();
这一行代码可以分为如下三行伪代码:

memory = allocate();//1.分配内存
initInstance(memory);//2.初始化
instance = memory;3.设置引用指向内存地址


我们知道在java代码的各级编译过程中都存在执行重排序的情况,如果上述代码中2和3被重排序,3先于2执行,并不违反单线程as-if-serial语义和多线程happens-before语义,再如果在2执行之前发生线程切换,另外一个线程在判断instance变量是否为Null时将返回false,并返回Instance,然而此时instance对应的内存区域并没有被初始化为Singleton对象。

那么我们应该如何处理这种情况呢?很简单,禁止
instance = new Singleton();
这行代码指令重排序,为此我们给instance变量引入happens-before语义限制—-就是用
volatile
关键字修饰instance变量。可靠的改进方式:

public class Singleton{
private static volatile Singleton instance = null;
private Singleton(){}
public static Singleton newInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance =  new Singleton();
}
}
}
return instance;
}
}


静态内部类形式

public class Singleton{
private static class SingletonHolder{
public static Singleton instance = new Singleton();
}
public static Singleton newInstance(){
return SingletonHolder.instance;
}
}


在这种形式中,巧妙地利用的jvm的类加载机制,实现了线程安全的懒汉式实例化。

当线程调用newInstance方法时,会触发静态内部类SingletonHolder的加载和初始化,这里不存在线程安全问题;同时当没有线程调用newInstance方法时,静态内部类将不会被加载。

总结

由上面的分析可知,静态内部类形式volatile关键字改进的双重检查锁定形式最能称得上是单例模式的最佳实践。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  单例模式