6种单例模式实现

普通懒汉式

public class Singleton {

    /** 单例对象 */
    private static Singleton instance;

    /**
     * 私有构造方法.
     */
    private Singleton() {
    }

    /**
     * 静态方法, 用于获取单利对象.
     * 如果单例对象未创建, 则创建新单例对象, 否则直接返回该对象.
     *
     * @return 单例对象.
     */
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

最简单的懒汉式单例,在首次调用 getInstance(); 时,会对单例对象进行实例化。
然而,这种方式明显无法在多线程模式下正常工作。当线程并发调用getInstance(); 时,由于线程之间没有进行同步,有可能两个线程同时进入 if 条件,导致实例化两次。

线程安全的懒汉式

public class Singleton {

    /** 单例对象 */
    private static Singleton instance;

    /**
     * 私有构造方法.
     */
    private Singleton() {
    }

    /**
     * 静态方法, 用于获取单利对象.
     * 如果单例对象未创建, 则创建新单例对象, 否则直接返回该对象.
     *
     * @return 单例对象.
     */
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

最简单的线程安全的懒汉模式,通过在 getInstance() 方法上添加 synchronized 关键字,保证同一时间仅有一个线程能够执行该代码段,以保证不会出现上面一种方法产生的问题。
然而,这种方法效率很低。每次调用 getInstance() 方法,都将为代码段加锁,同一时间该代码段只能被一个线程访问。然而除了首次调用外,都是不需要同步的,因为 instance 已经被实例化。

Double-Check

public class Singleton {

    /** 单例对象 */
    private static Singleton instance;

    /**
     * 私有构造方法.
     */
    private Singleton() {
    }

    /**
     * 静态方法, 用于获取单利对象.
     * 如果单例对象未创建, 则创建新单例对象, 否则直接返回该对象.
     *
     * @return 单例对象.
     */
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Double-check 即双重校验,该方法是针对上述方法提出的一种改进方案。
在 getInstance() 方法中,通过不加锁判断 instance 是否实例化。如果没有实例化,再进行加锁、实例化过程,以减少在实例化后调用 getInstance() 方法导致的性能损耗。

缺陷:instance = new Singleton()语句,看起来是一句代码,但实际上不是一个原子操作。它大致做了三件事:
1)给Singleton的实例分配内存
2)调用构造函数,初始化成员字段
3)将insance对象指向分配的内存空间
但是由于Java编译器允许处理器乱序执行,以及各种其他情况,在JDK1.5前上面的第二步和第三步没有办法保证执行顺序。当执行顺序是1-3-2时,如果执行到3后还没执行2.这时候有另一个线程调用了getInstance()方法,那么它判断到的instance不是null,它不需要经过同步代码块,直接获取到了这个错误的对象去做事去了。这就导致了错误出现。解决方法是private static Singleton instance;改为private static volatile Singleton instance;以及使用JDK1.5以上的版本。建议还是使用静态内部类的方式更好,可以规避上述问题

饿汉式

public class Singleton {

    /** 单例对象, 类装载时进行实例化. */
    private static final Singleton singleton = new Singleton();

    /**
     * 私有构造方法.
     */
    private Singleton() {
    }

    /**
     * 静态方法, 用于获取单利对象.
     *
     * @return 单例对象.
     */
    public static Singleton getInstance() {
        return singleton;
    }
}

饿汉式单例的原理是 ClassLoader 装载类是单线程,通过这种机制避免了线程同步问题。
这种方式虽然避免了线程同步问题,但却有可能带来性能问题。
无论该类是否被使用, ClassLoader 都有可能(也有可能被 ClassLoader 忽略)加载该类并实例化该单例对象。所以在基础类库场景下,这种方法会无故消耗更多的资源。

静态内部类方式

public class Singleton {

    /**
     * 私有构造方法.
     */
    private Singleton() {
    }

    /**
     * 静态方法, 用于获取单利对象.
     *
     * @return 单例对象.
     */
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }

    private static class SingletonHolder {

        /** 单例对象, 类装载时进行实例化. */
        private static final Singleton instance = new Singleton();
    }
}

这种方法同样利用了 ClassLoader 单线程装载的方式,避免了线程同步问题。然而他和上面一种方法不同的地方在于, instance 对象只有在 SingletonHolder 类被装载的时候才会被实例化。也就是说,只有当 getInstance() 方法调用时,才会被实例化,这样就避免了上述的资源损耗。

枚举方式

public enum Singleton {
   INSTANCE;
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值