性能优化计算机微刊程序员

单例模式——应用最广的模式

2017-09-16  本文已影响304人  一个有故事的程序员

导语

单例模式是应用最广的模式,在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个全局对象,这样有利于我们协调系统整体的行为。

主要内容

具体内容

单例模式的定义

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

单例模式的使用场景

他的作用是确保某个类只有一个实例,避免产生多个对象消耗过多的资源,或者某个类型的对象只应该有一个。例如,创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源,这时就要考虑使用单例模式。

单例模式的UML类图

单例模式的UML类图如下所示。

单例模式的UML类图

角色介绍:

实现单例模式主要关键点:

单例模式的实现方式

单例模式的实现有如下几种:
推荐使用DCL单例(双重检查锁定)和静态内部类单例模式。

饿汉模式
public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton(){
    }

    public static Singleton getInstance() {
        return instance;
    }
 }

这种写法是在类装载时就实例化instance,他避免了多线程的同步问题。但是不能保证有别的方式去装载,没有达到懒加载(延迟加载)。

懒汉模式(线程不安全)
public class Singleton {
    private static Singleton instance;

    private Singleton (){
    }

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

达到了懒加载,但是在多线程不能正常工作。

懒汉模式(线程安全)
public class Singleton {
    private static Singleton instance;

    private Singleton(){
    }

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

这种写法能够在多线程中很好的工作,但是每次调用getInstance方法都会进行同步,反应稍慢,还会造成不必要的开销,所以者这种不建议使用。

DCL单例(双重检查锁定)
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;
    }
}

这种写法在getSingleton方法中对singleton进行了两次判空,第一次是为了不必要的同步,第二次是为了在null的情况下创建实例。我们会发现上面代码有一个volatile关键字,因为在这里会有DCL失效问题,原因是Java编译器允许处理器乱序执行。那么为了解决这个问题,在JDK1.5之后,具体化了volatile关键字,只要定义时加上他,可以保证执行的顺序,虽然会影响性能。这种方式第一次加载时会稍慢,在高并发环境会有缺陷,但是一般能够满足需求。

静态内部类单例模式
public class Singleton {
    private Singleton(){
    }

    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    /**
    *静态内部类
    */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

这种是推荐使用的单例模式实现方式。当第一次加载Singleton类时并不会初始化INSTANCE,只有在第一次调用getInstance方法时才会导致INSTANCE被初始化。这种方式不仅能够保证线程安全,也能保证单例对象的唯一性,同时也延长了单例的实例化。

使用容器实现单例模式
public class SingletonManager {
    private static Map<String, Object> objMap = new HashMap<String,Object>();

    private Singleton() { 
    }

    public static void registerService(String key, Objectinstance) {
        if(!objMap.containsKey(key) ) {
            objMap.put(key, instance) ;
        }
    }

    public static ObjectgetService(String key) {
        return objMap.get(key) ;
    }
}

将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

总结

单例模式是运用频率很高的模式,但是,由于在客户端通常没有高并发的情况,因此,选择哪种实现方式都不会有太大的影响。即使如此,出于效率考虑,推荐使用DCL单例(双重检查锁定)和静态内部类单例模式。

更多内容戳这里(整理好的各种文集)

上一篇下一篇

猜你喜欢

热点阅读