Design_pattern之单例模式

许多系统只需要一个全局的对象,有利于协调系统整体的行为。这就需要单例模式。
单例模式最大的难点在于,确保线程安全。
单例模式大体分为两种:饿汉式和懒汉式 。
饿汉式适用于那些占用内存小初始化快的对象;在开始直接初始化,不会出现线程安全问题;

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);
    }
}

不管方式,核心都是私有构造方法,静态获取唯一。这个过程需要保证线程安全,防止反序列化。具体根据并发环境,资源消耗等选择。

你可能感兴趣的:(Design_pattern之单例模式)