单例模式

简介

单例模式属于创建者模式。单例类提供了一种访问其内部唯一的对象的方法,可以直接访问,无需实例化该类的对象。

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

关键在于私有化构造函数

实现方式

1.线程不安全的懒汉式实现方式

懒汉式表示不在一开始就创建对象,在需要的时候再创建

public class LazyMode {
    /**
     * 私有化构造方法
     */
    private LazyMode() {}

    /**
     * 静态私有的实例
     * static保证类没有创建实例也可以用这个实例
     */
    private static LazyMode INSTANCE = null;

    /**
     * public的访问单例的方法
     * 这个方法也必须是static的,不然无法返回static的INSTANCE
     *
     * 注意,这个方法是线程不安全的
     */
    public static LazyMode getInstance() {

        if (INSTANCE == null){
            INSTANCE = new LazyMode();
        }

        return INSTANCE;
    }

}

2.线程安全的懒汉式实现方式

public class LazyModeSafe {

    private LazyModeSafe() {}

    private static LazyModeSafe INSTANCE;

    public static synchronized LazyModeSafe getInstance() {
        if (INSTANCE == null){
            INSTANCE = new LazyModeSafe();
        }
        
        return INSTANCE;
    }
}

这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。

3.饿汉式

基于 classloader 机制避免了多线程的同步问题,但是没有做到lazy load

public class HungryMode {
    private HungryMode() {}

    private static HungryMode INSTANCE = new HungryMode();

    public static HungryMode getInstance() {
        return INSTANCE;
    }
}

4.双检锁/双重校验锁(DCL,即 double-checked locking)

public class DoubleCheckMode {
    private static volatile DoubleCheckMode instance;
    private DoubleCheckMode (){
    }
    public static DoubleCheckMode getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckMode.class) {
                if (instance == null) {
                    instance = new DoubleCheckMode();
                }
            }
        }
        return instance;
    }
}

这种写法在getSingleton方法中对singleton进行了两次判空,第一次是为了不必要的同步,第二次是在singleton等于null的情况下才创建实例

DCL优点是资源利用率高,第一次执行getInstance时单例对象才被实例化,效率高。缺点是第一次加载时反应稍慢一些,在高并发环境下也有一定的缺陷,虽然发生的概率很小。DCL虽然在一定程度解决了资源的消耗和多余的同步,线程安全等问题,但是他还是在某些情况会出现失效的问题,也就是DCL失效,在《java并发编程实践》一书建议用静态内部类单例模式来替代DCL。

5.静态内部类实现单例模式

public class StaticInternClass {

    private StaticInternClass() {}

    private static class LazyHandler {

        private static final StaticInternClass INSTANCE = new StaticInternClass();

    }

    public static StaticInternClass getInstance() {

        return LazyHandler.INSTANCE;

    }
}

使用静态内部类持有单例对象

第一次加载SingletonIntern类时并不会初始化INSTANCE

只有在第一次调用getInstance方法时虚拟机加载LazyHandler并初始化INSTANCE

这样不仅利用类加载机制保证了线程安全,还可以实现Lazy Load

6.枚举模式

public enum EnumMode {
    INSTANCE;
    public void doSomeThing() {
    }
}

枚举的方式是比较少见的一种实现方式,但是看上面的代码实现,却更简洁清晰。并且她还自动支持序列化机制,绝对防止多次实例化。

总结

使用饿汉式、双检锁、静态内部类这三种模式

posted @ 2020-09-15 15:36  swifthao  阅读(278)  评论(0编辑  收藏  举报
Live2D