设计模式

单例模式

2020-03-08  本文已影响0人  GeekerLou

单例模式

单例模式确保一个类只有一个实例,并提供一个全局访问点。

我们常常希望某个对象实例只有一个,不想要频繁地创建和销毁对象,浪费系统资源,最常见的就是 IO 、数据库的连接、Redis 连接等对象,完全没有必要创建多个一模一样的对象,一个足矣。

实现思路:(俗称"两私一公开")

要点:

典型实现

使用场景

实例

SimpleSingleton

/**
 * 非线程安全的简单的单例模式
 */
@NotThreadSafe
public class SimpleSingleton {

    private static SimpleSingleton instance;

    private SimpleSingleton(){}

    //当多个线程同时调用 getInstance() 方法时,可能会产生多个 instance 实例,因此这种方式并不是真正的单例。
    public static SimpleSingleton getInstance(){
        if(instance == null){
            instance = new SimpleSingleton();
        }
        return instance;
    }
}

LazySingleton

/**
 * 懒汉式的单例模式:只在首次调用获得对象实例的方法时才会实例化对象
 *
 *
 * 优点:
 * 1. 实现了延迟加载
 *
 * 缺点:
 * 1. 使用延迟加载而引入同步关键字synchronized反而降低了系统的性能
 */
@ThreadSafe
public class LazySingleton {
    /**
     * 私有化的构造函数,确保单例不会再系统中的其他地方被实例化
     */
    private LazySingleton() {
        System.out.println("LazySingleton is creating");
    }

    /**
     * instance初始值被赋值null,确保系统启动时没有额外的负载
     */
    private static LazySingleton instance = null;

    /**
     * 首先判断当前单例是否已经存在,若存在则直接返回,不存在则新建后再返回新建的
     * synchronized关键字保证了多线程环境下instance == null判断的安全性
     *
     * @return
     */
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

HungrySingleton

/**
 * 饿汉式单例模式:在单例类被加载时候,就实例化一个对象交给自身管理的引用
 *
 * 优点:简单,可靠。
 *
 * 缺点:无法对instance实例做延迟加载。具体而言
 * 首次,在首次使用到该单例类时会进行类加载,该加载的过程可能会很慢,从而影响到应用的启动速度;
 * 其次,该单例类在系统中还扮演者其他角色,那么任何使用到该单例类的地方都会初始化类的实例变量,而不管是否会用到。
 *
 */
@ThreadSafe
public class HungrySingleton {
    /**
     * 私有化的构造函数,确保单例不会再系统中的其他地方被实例化
     */
    private HungrySingleton() {
        System.out.println("HungrySingleton is created");
    }

    /**
     * private保证无法直接被访问,static保证只有一份
     */
    private static HungrySingleton instance = new HungrySingleton();

    /**
     * static允许通过类直接获取,public表明这是一个公共的允许访问的入口
     * @return
     */
    public static HungrySingleton getInstance() {
        return instance;
    }

    /**
     * 创建字符串的工具方法,模拟该单例扮演的其他角色
     * @return
     */
    public static String  createString(){
        System.out.println("createString in HungrySingleton");
        return "demo string";
    }
}

DoubleCheckedLockingSingleton

/**
 * double-checked locking( 双重检查加锁 )
 * 这种方式主要用到两个关键字 volatile 和 synchronized
 */
@ThreadSafe
public class DoubleCheckedLockingSingleton {

    private volatile static DoubleCheckedLockingSingleton instance;

    private DoubleCheckedLockingSingleton() {
    }

    public static DoubleCheckedLockingSingleton getInstance() {
        // 第一次判空检查,volatile保证了可见性,多个线程看到的是一致的结果
        // 如果判断为非空,多个线程拿到同一个对象也是OK的,所以第一次判断时无需加锁
        if (instance == null) {
            synchronized (DoubleCheckedLockingSingleton.class) {
                // 由于需要新建对象,因此需要加锁实现同步
                if (instance == null) {
                    instance = new DoubleCheckedLockingSingleton();
                }
            }
        }
        return instance;
    }
}

StaticClassSingleton

/**
 * 懒汉式的单例模式,采用内部类实现,可以实现延迟加载
 *
 * 实例的建立是在类加载时完成,故天生对多线程友好,无需使用synchronized关键字进行同步。
 * 即实现了延迟加载,也避免了同步锁的性能开销,正所谓一举两得。
 */
@ThreadSafe
public class StaticClassSingleton {
    /**
     * 私有化的构造函数,确保单例不会再系统中的其他地方被实例化
     */
    private StaticClassSingleton() {
        System.out.println("LazySingleton is creating");
    }

    /**
     * 使用内部类来维护单例的实例,当StaticSingleton被加载时,其内部类并不会被初始化,故可以确保党
     * StaticSingleton被JVM载入时,不会初始化instance。
     */
    private static class SingletonHolder {
        private static StaticClassSingleton instance = new StaticClassSingleton();
    }

    /**
     * 当getInstance()方法被调用时,才会加载SingletonHolder,从而初始化instance。
     * @return
     */
    public static StaticClassSingleton getInstance() {
        return SingletonHolder.instance;
    }
}

EnumSingleton

/**
 * 使用枚举实现的单例模式
 */
@ThreadSafe
public class EnumSingleton{
    // 私有的构造方法
    private EnumSingleton(){}

    // 内部枚举类
    private static enum Singleton{
        INSTANCE;

        private EnumSingleton singleton;

        //JVM会保证此方法绝对只调用一次
        private Singleton(){
            singleton = new EnumSingleton();
        }
        public EnumSingleton getInstance(){
            return singleton;
        }
    }

    // 公开的全局访问点
    public static EnumSingleton getInstance(){
        return Singleton.INSTANCE.getInstance();
    }

}

单元测试

/**
 * 单例模式的测试类
 */
public class SingletonTest extends BaseTest {

    private static final Integer LIMIT = 1000;

    @Test
    public void hungrySingletonTest() {
        long beginTime = System.currentTimeMillis();
        for (int i = 0; i < LIMIT; ++i) {
            HungrySingleton.getInstance();
        }
        System.out.println("hungrySingleton:" + (System.currentTimeMillis() - beginTime));
    }

    @Test
    public void lazySingletonTest() {
        long beginTime = System.currentTimeMillis();
        for (int i = 0; i < LIMIT; ++i) {
            LazySingleton.getInstance();
        }
        System.out.println("lazySingleton:" + (System.currentTimeMillis() - beginTime));
    }

    @Test
    public void enumSingletonTest() {
        EnumSingleton obj1 = EnumSingleton.getInstance();
        EnumSingleton obj2 = EnumSingleton.getInstance();
        //输出结果:obj1==obj2?true
        System.out.println("obj1==obj2?" + (obj1 == obj2));
    }
}

参考资料

  1. 代码仓库-单例模式
上一篇 下一篇

猜你喜欢

热点阅读