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

2023-12-13 03:49:13

单例模式

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

饿汉模式

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

例如

public class Singleton {
    private static Singleton singleton=new Singleton();
    
    private Singleton() {}
    
    public static Singleton getSingleton() {
        return singleton;
    }
}

注意:

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

懒汉模式

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

例如

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方法进行加锁

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为空的时候加锁再判断一次是否为空即可

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 (指令重排序)

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;
    }
}

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

文章来源:https://blog.csdn.net/djyyyg/article/details/134901356
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。