Java实现单例模式(懒汉式、饿汉式、双重检验锁、静态内部类方式、枚举方式)

目录

  • 懒汉式单例
    • 1、适用于单线程环境(不推荐)
    • 2、适用于多线程环境,但效率不高(不推荐)
    • 3、双重检验锁
    • 4、静态内部类方式(推荐)
  • 饿汉式单例
    • 1、饿汉式(推荐)
    • 2、枚举方式(推荐)
  • 总结


懒汉式单例

懒汉式单例模式在第一次调用的时候进行实例化。

/**
 * Title:Singleton1
* Description:单例模式——懒汉式 * * @author QiuChangjin * @date 2018年4月17日 */
public class Singleton1 { private static Singleton1 instance = null; private Singleton1() { } /** * 1、适用于单线程环境(不推荐) */ public static Singleton1 getInstanceA() { if (null == instance) { instance = new Singleton1(); } return instance; } /** * 2、适用于多线程环境,但效率不高(不推荐) */ public static synchronized Singleton1 getInstanceB() { if (instance == null) { instance = new Singleton1(); } return instance; } /** * 3、双重检查加锁(推荐) */ public static Singleton1 getInstanceC() { // 先判断实例是否存在,若不存在再对类对象进行加锁处理 if (instance == null) { synchronized (Singleton1.class) { if (instance == null) { instance = new Singleton1(); } } } return instance; } }

1、适用于单线程环境(不推荐)

此方式在单线程的时候工作正常,但在多线程的情况下就有问题了。如果两个线程同时运行到判断instance是否为null的if语句,并且instance的确没有被创建时,那么两个线程都会创建一个实例,此时类型Singleton1就不再满足单例模式的要求了。

    public static Singleton1 getInstanceA() {
        if (null == instance) {
            instance = new Singleton1();
        }
        return instance;
    }
}

2、适用于多线程环境,但效率不高(不推荐)

为了保证在多线程环境下我们还是只能得到该类的一个实例,只需要在getInstanceB()方法加上同步关键字sychronized,就可以了。每次调用getInstanceB()方法时都被synchronized关键字锁住了,会引起线程阻塞,影响程序的性能。

public static synchronized Singleton1 getInstanceB() {
        if (instance == null) {
            instance = new Singleton1();
        }
        return instance;
    }

3、双重检验锁

为了在多线程环境下,不影响程序的性能,不让线程每次调用getInstanceC()方法时都加锁,而只是在实例未被创建时再加锁,在加锁处理里面还需要判断一次实例是否已存在。

    public static Singleton1 getInstanceC() {
        // 先判断实例是否存在,若不存在再对类对象进行加锁处理
        if (instance == null) {
            synchronized (Singleton1.class) {
                if (instance == null) {
                    instance = new Singleton1();
                }
            }
        }
        return instance;
    }

4、静态内部类方式(推荐

加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。 由于在调用 StaticSingleton.getInstance() 的时候,才会对单例进行初始化,而且通过反射,是不能从外部类获取内部类的属性的;由于静态内部类的特性,只有在其被第一次引用的时候才会被加载,所以可以保证其线程安全性。
总结:
优势:兼顾了懒汉模式的内存优化(使用时才初始化)以及饿汉模式的安全性(不会被反射入侵)。
劣势:需要两个类去做到这一点,虽然不会创建静态内部类的对象,但是其 Class 对象还是会被创建,而且是属于永久带的对象。

/**
 * Title:StaticSingleton
* Description:单例模式——静态内部类方式 * * @author QiuChangjin * @date 2018年4月17日 */
public class StaticSingleton { /** * 私有构造方法,禁止在其他类中创建实例 */ private StaticSingleton() { } /** * 获取实例 */ public static StaticSingleton getInstance() { return StaticSingletonHolder.instance; } /** * 一个私有的静态内部类,用于初始化一个静态final实例 */ private static class StaticSingletonHolder { private static final StaticSingleton instance = new StaticSingleton(); } /** * 方法A */ public void methodA() { } /** * 方法B */ public void methodB() { } public static void main(String[] args) { StaticSingleton.getInstance().methodA(); StaticSingleton.getInstance().methodB(); } }

饿汉式单例

1、饿汉式(推荐)

饿汉式单例类:在类初始化时,已经自行实例化。

/**
 * Title:Singleton2
* Description:单例模式——饿汉式 * * @author QiuChangjin * @date 2018年4月17日 */
public class Singleton2 { private static final Singleton2 instance = new Singleton2(); private Singleton2() { } public static Singleton2 getInstance() { return instance; } }

2、枚举方式(推荐)

创建枚举默认就是线程安全的,所以不需要担心double checked locking,而且还能防止反序列化导致重新创建新的对象。保证只有一个实例(即使使用反射机制也无法多次实例化一个枚举量)。

/**
 * Title:Singleton
* Description:单例模式——枚举方式 * * @author QiuChangjin * @date 2018年4月17日 */
public class Singleton { public static void main(String[] args) { Single single = Single.SINGLE; single.print(); } enum Single { SINGLE; private Single() { } public void print() { System.out.println("hello world"); } } }

总结

就我个人而言,一般情况下直接使用饿汉式就好了,如果明确要求要懒加载(lazy initialization)会倾向于使用静态内部类,如果涉及到反序列化创建对象时会试着使用枚举方式来实现单例。

你可能感兴趣的:(java基础)