单例模式——懒汉模式的双重检测锁问题

单例模式

单例模式顾名思义就是只实例化一个对象,通过把构造方法私有化来禁止创建实例

饿汉模式

饿汉模式的特点是在类加载的时候就创建并初始化一个实例,实例在整个程序运行期间都是唯一的

例如

java 复制代码
public class Singleton {
    private static Singleton singleton=new Singleton();
    
    private Singleton() {}
    
    public static Singleton getSingleton() {
        return singleton;
    }
}

注意:

这里的getSingleton方法必须是静态方法 ,因为静态方法可以通过 类名.方法 的方法使用,而非静态方法则要创建实例,这与我们单例模式的规则不相符

懒汉模式

懒汉模式的特点是需要的时候在创建实例,实例在整个程序运行期间都是唯一的

例如

java 复制代码
public class Singleton {
    private static Singleton singleton=null;
    private Singleton() {}

    public static Singleton getSingleton() {
        if (singleton==null) {
            singleton=new Singleton();
        }
        return singleton;
    }
}

上面代码在单线程模式下是没问题的,但是在多线程模式下就会存在线程安全问题

如果在首次创建实例,多个线程同时调用getSingleton方法,就有可能创建多个实例

改进1 (创建多个实例)

因此我们可以进行对getSingleton方法进行加锁

java 复制代码
public class Singleton {
    private static Singleton singleton=null;
    private Singleton() {}

    public static synchronized Singleton getSingleton() {
        if (singleton==null) {
            singleton=new Singleton();
        }
        return singleton;
    }
}

这样虽然解决刚刚线程安全的问题,但每次调用getSingleton方法都要加锁,增加不少的开销

我们发现上面线程安全问题只存在于首次创建实例的情况,因此我们只需要对singleton为空的时候单独处理即可

改进2 (性能低)

因此我们可以当singleton为空的时候加锁再判断一次是否为空即可

java 复制代码
public class Singleton {
    private static Singleton singleton=null;
    private Singleton() {}

    public static Singleton getSingleton() {
        if (singleton==null) {
            synchronized (Singleton.class) {
                if (singleton==null) {
                    singleton=new Singleton();
                }
            }
        }
        return singleton;
    }
}

粗略的看上去好像没什么问题,实际上这里还有一个指令重排序的坑

通过查阅资料知道singleton=new Singleton();

这个代码在执行的时候实际是执行3句伪代码

memory=allocate(); //1.分配对象的内存空间

ctorInstance(memory); //2. 初始化对象

instance=memory; //3. 设置instance指向刚分配的内存地址

JVM在执行的时候可能就会优化成 1 3 2的顺序执行

可能导致在多线程环境下,还没执行2就已经被其他线程返回了一个刚分配的地址

同样存在线程安全问题,这就需要我们使用volatile关键字来禁止指令重排序

改进3 (指令重排序)

java 复制代码
public class Singleton {
    private static volatile Singleton singleton=null;
    private Singleton() {}

    public static Singleton getSingleton() {
        if (singleton==null) {
            synchronized (Singleton.class) {
                if (singleton==null) {
                    singleton=new Singleton();
                }
            }
        }
        return singleton;
    }
}

这样一个线程安全的懒汉模式就完成了

相关推荐
CallFay云起未来6 小时前
AI客服能不能减少人工回复?从重复咨询到人机协同的落地分析
java·大数据·人工智能·文心一言
我命由我123456 小时前
Android 开发 - 获取当前设备屏幕的旋转角度
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
SLD_Allen6 小时前
Go语言runtime全景
开发语言·javascript·golang
Elastic 中国社区官方博客6 小时前
OpenTelemetry Java 扩展:无需分叉 agent 即可自定义追踪
java·大数据·运维·开发语言·数据库·人工智能·elasticsearch
京东云开发者6 小时前
上游给空、下游拿到 -1,我把故障一直追到了 commons-beanutils 的构造函数
java·ai编程
阿无,6 小时前
Java多线程面试题之AQS
java·开发语言
数据狐(Datafox)6 小时前
京东商品列表API技术解析与落地应用(含标准 JSON 示例)
java·大数据·前端·人工智能·python·数据分析·json
风萧何6 小时前
架构艺术,是权衡的艺术。
前端·javascript·架构
偷偷挖矿的牛马矿工6 小时前
execa NodeJs新一代child_process的强力替代品
javascript
摇滚侠6 小时前
《SpringBoot 3:入门与应用实战》第 7 章 AOP 思想与实现 阅读笔记 15
java·spring boot·笔记