设计模式之单例模式的九种写法对比:

240 阅读3分钟

背景说明:因为在Android服务启动的过程中调查服务启动相应的时间慢,过程中定位到单例初始化慢,猜测可能是单例写法有问题,所有整理一下单例的写法,供参考。

使用推荐:最终综合线程安全、效率、写法简单等因素对比,推荐使用 六、双重检查(DCL)或者 七、静态内部类

一、饿汉式(静态常量)

`class King { private static final King kingInstance = new King();

static King getInstance() {
    return kingInstance;
}


private King() {
}

}`

优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。

缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

二、饿汉式(静态代码块)

`class King { private static King kingInstance;

static {
    kingInstance = new King();
}

private King() {
}

public static King getKingInstance() {
    return kingInstance;
}

}`

优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。

缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

三、懒汉式(线程不安全)

`public class King { private static King kingInstance;

private King() {
}

public static King getKingInstance() {
    if (kingInstance == null) {
        kingInstance = new King();
    }
    return kingInstance;
}

}` 优点:懒加载,只有使用的时候才会加载。

缺点:但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。

四、懒汉式(线程安全)

`public class King { private static King kingInstance;

private King() {
}

public static synchronized King getKingInstance() {
    if (kingInstance == null) {
        kingInstance = new King();
    }
    return kingInstance;
}

}` 优点:懒加载,只有使用的时候才会加载,获取单例方法加了同步锁,保障线程安全。

缺点:效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。

五、懒汉式(线程安全,同步代码块)

`public class King { private static King kingInstance;

private King() {
}

public static King getKingInstance() {
    if (kingInstance == null) {
        synchronized (King.class) {
            kingInstance = new King();
        }
    }
    return kingInstance;
}

}` 优点:改进了第四种效率低的问题。

缺点:不能完全保证单例,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。

六、双重检查(DCL)

`public class King {

private static volatile King kingInstance;

private King() {
}

public static King getKingInstance() {
    if (kingInstance == null) {
        synchronized (King.class) {
            if (kingInstance == null){
                kingInstance = new King();
            }
        }
    }
    return kingInstance;
}

}` 优点:线程安全;延迟加载;效率较高。

缺点:JDK < 1.5 的时候不可用

不可用原因:由于volatile关键字会屏蔽Java虚拟机所做的一些代码优化,可能会导致系统运行效率降低,而JDK 1.5 以及之后的版本都修复了这个问题。

图片

七、静态内部类

`public class King {

private King() {
}

private static class KingInstance{
    private static final King KINGINSTANCE = new King();
}

public static King getInstance(){
    return KingInstance.KINGINSTANCE;
}

}` 优点:避免了线程不安全,延迟加载,效率高。

缺点:暂无,最推荐使用。

特点:这种方式跟饿汉式方式采用的机制类似,但又有不同。

两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

八、枚举 /**

  • 枚举 */ public enum King { KINGINSTANCE; } 优点:不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。

缺点:JDK 1.5之后才能使用。

九、容器类管理 使用容器实现单例模式(可以用于管理单例,有兴趣的可以尝试一下)

class InstanceManager { private static Map<String, Object> objectMap = new HashMap<>(); private InstanceManager(){} public static void registerService(String key,Object instance){ if (!objectMap.containsKey(key)){ objectMap.put(key,instance); } } public static Object getService(String key){ return objectMap.get(key); } }

/**

  • 使用方式

  • Dog类就不贴出来了

  • 自己随便写个就行

  • 可以运行一下看看 打印的地址是否一致 */ class Test { public static void main(String[] args) {

      InstanceManager .registerService("dog", new Dog());
    
      Dog dog = (Dog) InstanceManager .getService("dog");
      Dog dog2 = (Dog) InstanceManager .getService("dog");
      Dog dog3 = (Dog) InstanceManager .getService("dog");
      Dog dog4 = (Dog) InstanceManager .getService("dog");
    
      System.out.println(dog);
      System.out.println(dog2);
      System.out.println(dog3);
      System.out.println(dog4);
    

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

缺点:不常用,有些麻烦

总结1:九种单例模式的效率对比:

image.png

实例2:Android源码中的单例使用:

image.png

设计模式优缺点3:单例本身的优缺点说明:

image.png

原技术小程序地址:mp.weixin.qq.com/s/WxG8LHNJV…