JAVA Singleton

单例模式

作为比较经典的设计模式之一,能够保证一个类只有一个实例(对于一个ClassLoader来说)。

1.通过定义私有的构造函数(private constructor),使从单例类的外部无法初始化该类,从而确保该类只有一个实例。

2.提供公有、静态(private, static)方法访问该类的唯一实例。

基本实现:

public final class Singleton1 {
    private static final Singleton1 INSTANCE = new Singleton1();
    private Singleton1(){}
    public static Singleton1 getInstance(){
        return INSTANCE;
    }
}

lazy-load

单例类的实现经常采用lazy-load的方式,从而在getInstance方法第一次被调用的时候才实例化(如果从未调用getInstance方法,则不会实例化该类)。lazy-load如果在multi-thread的环境下实现,需要采取一些措施来避免竞争条件(race condition)。

目前公认的比较好的实现方式是:

public class Singleton2 {
    private static volatile Singleton2 INSTANCE;
    private Singleton2(){}
    public Singleton2 getInstance() {
        Singleton2 localRef = INSTANCE;
        if (localRef == null) {
            synchronized(this) {
                localRef = INSTANCE;
                if (localRef == null) {
                    INSTANCE = localRef = new Singleton2();
                }
            }
        }
        return localRef;
    }
}

需要注意的是, localRef是有存在必要的。因为Java的volatile关键字确保了每次获取INSTANCE对象都是从主存获取(避免了多线程条件下可能存在的缓存/主存中存储的INSTANCE对象不一致的问题),但这也导致了读取速度的相对缓慢。使用localRef并最终返回localRef而不是INSTANCE,从而提高该实现的性能。

Initialization-on-demand holder idiom

另外一种比较合适的方式采用比较有名的initialization-on-demand holder idiom方式:

public class Singleton3 {
    private Singleton3(){}
    private static class LazyHolder {
        public static final Singleton3 INSTANCE = new Singleton3();
    }
    public static Singleton3 getIntance() {
        return LazyHolder.INSTANCE;
    }
}

这种实现,利用了JVM的特性,在第一次调用getInstance方法的时候才实例化LazyHolder及INSTANCE。JVM保证类实例化的过程是顺序的,非并发的。从而确保了实例的唯一性,也避免了额外的synchronization开销。这是一种有效的、线程安全的单例实现方式。

double check locking

之前曾经看过一些关于Singleton pattern相关的文章,可能是比较早的文章,推荐的方式是采用double check locking的方式实现Singleton lazy load. 但是这种方式存在一些已知的问题。

使用 double check locking的目是避免每次调用getInstance方法,在检查INSTANCE的时候就竞争锁,但是这种设计模式在某些情况下是不安全的,有时也被认为是anti-pattern的。

如:

public class Singleton4 {
    private static Singleton4 INSTANCE=null;
    private Singleton4(){}
    public static Singleton4 getIntance(){
        if(INSTANCE==null){
            synchronized (Singleton4.class){
                if(INSTANCE==null){
                    INSTANCE = new Singleton4();
                }
            }
        }
        return  INSTANCE;
    }
}
来自Double check locking wiki 的解释:
线程A检查到INSTANCE未初始化,获取lock并开始初始化INSTANCE。
Java允许更新shared variable指向一个并未完全构造完毕(partially constructed)的对象。

即存在一种可能,线程B在线程A并未完全初始化完成INSTANCE的时候即通过getInstance方法获取并使用,就可能导致程序出错崩溃。

在J2SE 1.4 (及以前),错误的double-checked locking实现可能存在一些难以察觉的、潜在的错误,经常会产生貌似正确的结果。同时multi-thread环境复杂,还可能因为编译器的实现,线程调度方法,其他并发事件的影响,错误经常是偶尔出现的,重现困难。

https://en.wikipedia.org/wiki/Singleton_pattern

https://en.wikipedia.org/wiki/Double-checked_locking

https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值