栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

线程安全的单例模式

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

线程安全的单例模式

单例模式,即我们只允许一个类有且仅有一个实例对外提供服务。通常为了性能考虑,单例模式会以懒加载的形式创建,也即单例中的懒汉模式,与之相对的当然就是单例的饿汉模式:不管用不用,我可以先给你创建出来。

1.单线程下的单例模式实现

非多线程环境下,由于不存在线程对共享数据(对象)的竞争,所以也就没有线程安全问题,其单例模式实现如下:

public final class FooSingleton {

    //持有类的唯一实例
    private static FooSingleton instance = null;

    
    private FooSingleton() {
        //do nothing
    }

    
    private static FooSingleton getInstance(){
        if (null == instance) {//非线程安全
            instance = new FooSingleton();
        }
        return instance;
    }

    public void methodOther() {
        //类提供的实例方法,调用方获取单例对象后,可调用类中的实例方法
    }

}
2.多线程下的单例模式实现

可以发现,在多线程环境下,这个获取单例的方法不是线程安全的:

private static FooSingleton getInstance(){
        if (null == instance) {//非线程安全
            instance = new FooSingleton();
        }
        return instance;
    }

这就会导致客户端(也就是各个调用端)获取到的对象并不是同一个对象,这显然就违背了我们使用单例模式的初衷了。怎样保证线程安全呢?常用的实现方法有如下三种:

2-1 直接加锁实现线程安全的单例模式

直接加锁实现线程安全的单例模式:

public final class FooSingletonSafety {

    //持有类的唯一实例
    private static FooSingletonSafety instance = null;

    
    private FooSingletonSafety() {
        //do nothing
    }

    
    private static FooSingletonSafety getInstance(){
        synchronized (FooSingletonSafety.class) {
            if (null == instance) {
                instance = new FooSingletonSafety();
            }
        }

        return instance;
    }

    public void methodOther() {
        //类提供的实例方法,调用方获取单例对象后,可调用类中的实例方法
    }

}

显然,直接加锁是可以保证单例模式的线程安全的,但是你可能会发现,这种实现方式下,每个线程在获取单例对象的时候都要去抢锁,获取之后再释放锁,这显然效率是不高的(每次获取对象都要有锁开销)。所以,一般我们会通过下面的DCL去实现线程安全的单例模式

2-2 DCL实现线程安全的单例模式

DCL,即double-check-locking双重检查锁定,代码实现如下:

public final class FooSingletonBasedDCL {

    //持有类的唯一实例
    private static volatile FooSingletonBasedDCL instance = null;

    
    private FooSingletonBasedDCL() {
        //do nothing
    }

    
    private static FooSingletonBasedDCL getInstance(){
        if (null == instance) {
            synchronized (FooSingletonSafety.class) {
                if (null == instance) {
                    //new关键字对应三条指令:1.分配对象所需的存储空间  2.实例化对象  3.将对象引用赋值给变量
                    //这三条指令正常执行顺序是1->2->3,但是JIT编译器或处理器在执行时可能会将其优化为1->3->2
                    //所以,为了保证instance的可见性以及禁止重排序,所以instance变量必须要用volatile修饰
                    //否则,极端情况下,一个线程在判断if (null == instance)时,instance不为空直接返回,但此时
                    //instance可能是个半对象(对象并没有经过构造方法初始化),这将会造成代码错误
                    instance = new FooSingletonBasedDCL();
                }
            }
        }

        return instance;
    }

    public void methodOther() {
        //类提供的实例方法,调用方获取单例对象后,可调用类中的实例方法
    }

}

DCL你可能开发中基本没用过,但是这种实现方式在很多开源框架如Spring、dubbo等中都有使用

2-3 利用类加载机制实现线程安全的单例模式

利用Java的类加载机制:加载->链接->初始化,我们可以轻易的实现线程安全的单例模式,这种单例模式形式最简单,代码实现如下:

public final class BarSingleton {
    //利用类初始化只会发生一次,创建线程安全的单例
    private static final BarSingleton INSTANCE = new BarSingleton();

    public static BarSingleton getInstance() {
        return INSTANCE;
    }

    public void methodOther() {
        //类提供的实例方法,调用方获取单例对象后,可调用类中的实例方法
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1007801.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号