单例模式

2017-08-10  本文已影响6人  ilaoke

Ensure a class only has one instance, and provide a global point of access to it.

确保一个类只有一个实例,并且提供一个全局指针访问它

各种单例类的共同点:

  1. 私有构造器
  2. 私有的静态实例变量
  3. public的静态方法,返回单例对象

单例1

/**
 * 基础单例类
 */
public class BasicSingleton {
    private static BasicSingleton instance;

    // 私有构造器,确保不会在该类之外实例化
    private BasicSingleton() {
    }

    public static BasicSingleton getInstance() {
        // Lazy initialization 懒加载
        if (instance == null) {
            instance = new BasicSingleton();
        }
        return instance;
    }
}

注意构造器是private的,该单例的问题:不是线程安全的,当有多个线程调用getInstance()方法时,可能会产生多个实例。

单例2 - 线程安全 - 粗粒度

/**
 * 线程安全单例
 */
public class ThreadSafeSingleton {

    private static ThreadSafeSingleton instance;

    // 私有构造器,确保不会在该类之外实例化
    private ThreadSafeSingleton() {
    }

    // synchronized方法
    public static synchronized ThreadSafeSingleton getInstance() {
        // Lazy initialization 懒加载
        if (instance == null) {
            instance = new ThreadSafeSingleton();
        }
        return instance;
    }
}

线程安全单例,该单例的问题:在多线程情况下,会在getInstance()方法处形成阻塞。

单例3 - 线程安全 - 细粒度

/**
 * 加载线粒度的锁
 */
public class ThreadSafeSingleton2 {
    private static ThreadSafeSingleton2 instance;

    // 私有构造器,确保不会在该类之外实例化
    private ThreadSafeSingleton2() {
    }

    public static ThreadSafeSingleton2 getInstance() {
        if (instance == null) {
            // check 1
            synchronized (ThreadSafeSingleton2.class) {
                // double checked locking,再次判空是需要的
                // check 2
                if (instance == null) {
                    instance = new ThreadSafeSingleton2();
                }
            }
        }
        return instance;
    }
}

首先要明白细粒度的意思,单例2 - 线程安全 - 粗粒度 例子中,每次进入getInstance() 方法都需要同步,太浪费。这个例子中,判断完是否为空后再同步,这样大家需要同步的机会就大大减少了。同时要注意,再次判断是否为空是需要的,如果两个线程同时到达check 1,如果没有check 2的判空,就会产生两个实例。

单例4 - Eager initialization

/**
 * 提前初始化单例
 */
public class EagerInitializedSingleton {
    // 类加载即创建单例
    private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();

    // 私有构造器,确保不会在该类之外实例化
    private EagerInitializedSingleton() {
    }

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

该单例是线程安全的,但问题是即使在不需要实例时,也被初始化了。

单例5 - 静态块单例

/**
 * 静态块单例
 */
public class StaticBlockSingleton {
    private static StaticBlockSingleton instance;

    private StaticBlockSingleton() {
    }

    // 静态块的作用是,添加异常处理
    static {
        try {
            instance = new StaticBlockSingleton();
        } catch (Exception e) {
            throw new RuntimeException("Exception occured in creating singleton instance");
        }
    }

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

该单例的问题同 单例4 - Eager initialization,即使在不需要实例时也被初始化了,不同的是增加了异常处理的机会。

单例6 - Bill Pugh - 最佳实践

/**
 * 通过内部静态类来避免提前实例化和线程安全问题,
 * 从而达到只在需要时才被加载并且没有线程安全问题
 */
public class BillPughSingleton {

    // 私有构造器,确保不会在该类之外实例化
    private BillPughSingleton() {
    }

    // 内部静态类
    private static class SingletonHelper {
        private static final BillPughSingleton INSTANCE = new BillPughSingleton();
    }

    public static BillPughSingleton getInstance() {
        // 此时才会加载SingletonHelper类,然后创建单例对象
        return SingletonHelper.INSTANCE;
    }
}

内部静态类,只有在使用时才会被加载,然后创建单例。避免了提前创建和线程安全的问题。


参考:Java Singleton Design Pattern Best Practices with Examples

上一篇下一篇

猜你喜欢

热点阅读