懒汉式单例模式下的双检锁机制的理解

前言

我在学习懒汉式单例模式时,发现了懒汉双检锁的实现方式,当时不是很理解,查阅资料后得出以下结论

双检锁机制

在多个线程同时请求一个获取一个懒汉式单例对象时,可能会导致创建多个对象的问题,为了确保只有一个对象被创建,可以使用双检锁机制。(双检锁机制不是创建两个锁实现,而是再方法内部实现两种检锁)

双检锁机制基于懒汉模式,在第一次获取单例对象时才创建它;

为了提高程序的性能,我们不在方法前使用syschronized关键字,而是在方法内部使用,同时也在方法内部实现了双重检查锁定机制。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
//第一重检锁
if (uniqueInstance == null) {
//第二重检锁,只有uniqueInstance为null时才会生效
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}

在双检锁机制中,我们使用了synchronized关键字来创建一个锁,确保只有一个线程能进入if语句块内部。在此之前,我们首先检查uniqueInstance变量是否为null。如果为null我们才进入if语句块,获取锁,并再次检查uniqueInstance变量是否为null。这是因为在多线程环境下,可能会有多个线程同时进入第一个if语句块,如果不在此检查uniqueInstance变量是否为null,就有可能会创建多个Singleton对象,违反了单例模式的原则。

一旦我们进入了if语句块,获取了锁,就可以创建唯一的Singleton对象。其他线程尝试进入if语句块时,由于uniqueInstance变量已经不为null,所以它们不会执行if语句块内的代码,而是直接返回uniqueInstance变量的值。

在整个过程中,synchronized关键字可以确保只有一个线程能够访问if一句话内的代码,在这之前和之后,uniqueInstance变量由于是用volatile关键字进行了修饰,可以被确保它在多线程环境下的可见性,从而避免了问题的发生。

补充

除了单例模式之外,它还可以用于其他需要保证线程安全的场景。例如,我们可以使用双检锁机制来确保在多线程环境下只创建一个实例化某个对象的方法。
双检锁机制在多线程环境下会有比较好的性能表现,因为它只在第一次创建对象时使用synchronized关键字来获取锁,在之后的访问中不需要获取锁,因此避免了不必要的开销。如果在多线程环境下需要频繁创建对象,使用双检锁机制会比使用简单的synchronized关键字更加高效。
需要注意的是,虽然双检锁机制可以确保在多线程环境下只创建一个对象,但是如果对象的初始化操作耗时较长,可能会造成性能问题。此外,还需要注意正确使用volatile关键字,确保变量的可见性。

总结

总之,双检锁机制是一种比较常见的保证线程安全的方式,可以用于单例模式等需要确保只有一个对象被创建的场景。在使用双检锁机制时,需要注意并发情况下的安全性和性能问题。