您的位置:首页 > 其它

Design_pattern之单例模式

2016-04-03 18:07 281 查看
许多系统只需要一个全局的对象,有利于协调系统整体的行为。这就需要单例模式。

单例模式最大的难点在于,确保线程安全。

单例模式大体分为两种:饿汉式和懒汉式 。

饿汉式适用于那些占用内存小初始化快的对象;在开始直接初始化,不会出现线程安全问题;

public class Singleton_ehan {
//饿汉式
private static  Singleton_ehan instance=new Singleton_ehan();
private Singleton_ehan(){}
public static Singleton_ehan getInstance(){
return instance;
}

}


而懒汉式适用于这个对象用的不是太频繁或者只在特定场景使用,或者占用资源较大,初始化慢;

public class Sington_lanhan {
//懒汉式
private static Sington_lanhan instance=null;
private Sington_lanhan(){}
public static Sington_lanhan getInstance(){
if(instance==null){
instance=new Sington_lanhan();
}
return instance;
}
}


懒汉式存在线程安全的问题,需要加锁。

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


而加锁就会降低性能。如果每次获取都要同步就必然影响性能。

因此懒汉式又可以分为

-1.Double Check Lock (DCL)方式 是使用最多的单例方式

第一层判空主要为了不必要的同步;第二层判空为了在null的时候创建实例。

其中要注意volatile 关键字;这是由于instance=newSingleton_doubleValid();并不是一个原子操作,他被拆分成多条汇编指令,包括

- 分配内存

- 初始化成员和构造方法

- instance指向内存(此时i已经nstance!=null)

由于java编译器允许乱序执行,java内存模型(JMM)无法保证按顺序执行上边的操作,因此当1-3-2执行,线程A执行到3而线程B开始执行,就会出现问题。这就是DCL失效问题。在JDK1.5之后java具体化了了volatile 关键字,保证instance 每次从主内存中读取。

public class Singleton_doubleValid {
private volatile static Singleton_doubleValid instance=null;
private Singleton_doubleValid(){}
public static Singleton_doubleValid getInstance(){
if(instance==null){
synchronized (this){
if(instance==null){
instance=new Singleton_doubleValid();
}
}
}
return instance;
}
}


2,静态内部类的单例

在java并发编程实践中,支出DCL失效问题,不赞成使用,而是建议使用静态内部类的方式:

当第一次加载类的时候不会初始化,只有在第一次调用getInstance方法的时候才会初始化(虚拟机会加载holder类)。不仅保证了线程安全,也保证了单利的唯一性,延迟了单例的实例化。

public class Singleton_innerClass {
//静态内部类 在加载的时候初始化实例
private static class Sinleton_innerClass_Holder{
private static Singleton_innerClass instance=new Singleton_innerClass();
}
private Singleton_innerClass(){}
public Singleton_innerClass getInstance(){
return Sinleton_innerClass_Holder.instance;
}
}


3 枚举方式

枚举和普通的类一样,可以有字段和方法。但他是默认线程安全的,任何时候只偶有一个实例。写法简单是他最大的有点。

上述所有方法在反序列化的时候会重新生成新的对象。而枚举不会。具体不太懂。

public enum Singleton_Enum {
instance;
public void doSomething(){

}
}


4 容器方式

这是一个单例的管理类,在程序的初始将许多单例类型存入map中,使用的时候通过key取出。使用统一的接口操作,隐藏了具体实现,降低耦合。

public class Singleton_Manager<T> {
private static Map<String,Object>map=new HashMap<>();
private Singleton_Manager(){}
public static void registerInstance(String key,Object o){
if(!map.containsKey(key)){
map.put(key,o);
}
}
public static Object getInstance(String key){
return  map.get(key);
}
}


不管方式,核心都是私有构造方法,静态获取唯一。这个过程需要保证线程安全,防止反序列化。具体根据并发环境,资源消耗等选择。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: