常见的设计模式(二)—单例模式

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

优点: 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。 2、避免对资源的多重占用(比如写文件操作)。

缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

实现方式

 1.懒汉式:线程不安全,懒加载

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

2.线程安全懒汉式:效率很低

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

3.饿汉式

基于classloader机制,线程安全,没有锁,效率相对提高。但在类加载就初始化,浪费内存。

public class Singleton{
    private static Singleton instance=new Singleton();
    private Singleton(){};
    public static Singleton getInstance(){
        return instance;
    }
}

关于其名字由来:懒汉是在你真正用到的时候才去建这个单例对象。饿汉是不管你用不用的上,一开始就创建一个单例对象。

4.双重校验锁(DCL即double checked locking)

安全且在多线程情况下能保持高性能。

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

 1.为啥要判断两次空?

在同步块前判空是为了提高性能,如果没有判空,每个进入getInstance()都会得到一个静态内部锁,先判空能大大减少同步块的执行次数。第二次同步块里的判空是保证多线程的单例。

2.为啥要加volatile关键字

至于volatile关键字是避免指令重排,这就要归咎于JAVA的内存模型允许所谓的“无序写入”。具体情况可以看看这篇博文

5.静态内部类

public class Singleton{
    private static class SingletonHolder{
        private static final Singleton INSTANCE=new Singleton();
    }
    private Singleton(){}
    public static final Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }    
}

这种方式能达到双重锁一样效果,对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。

只有在要明确实现 lazy loading 效果时,才会使用这种方式。

6.枚举

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}  

它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。

posted on 2018-02-06 17:30  dfsgfsgsg  阅读(171)  评论(0)    收藏  举报

导航