懒汉式单例模式的主要好处有以下几点:
-
1.资源利用效率高:
只有在第一次调用
getInstance()
方法时才创建实例对象,而不是在类加载时就创建。这可以节省系统资源。 -
2.延迟加载:
实例对象的创建被延迟到第一次使用时,可以减少系统启动时的资源消耗。
-
3.线程安全:
这种实现方式在多线程环境下是线程安全的。通过对
getInstance()
方法的同步控制,可以确保在多个线程同时访问时,只有一个线程能够创建实例对象。
第一版懒汉模式的代码:
好处:只有instance为空的时候才new一个对象。
缺点:这是线程不安全的。
原因:如果两个线程同时执行 if (instance == null)
检查,并且 instance
恰好是 null
,那么两个线程都会创建新的 Singleton
实例,违背了单例模式的设计目的。
java
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
第二版懒汉模式的代码:
加synchronized关键字进行加锁操作。
好处:这样可以确保在同一时刻只有一个线程能够创建实例对象,从而保证线程安全。
缺点:
1.在高并发的场景下,对 getInstance()
方法加synchronized关键字会带来性能问题。
2.因为每次调用 getInstance()
方法都需要获取锁,这会导致不必要的性能开销。
java
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
最终版懒汉模式的代码:
为了解决性能问题,可以采用双重检查锁定(Double-Checked Locking, DCL)的方式
在这种实现中,我们先进行一次 instance == null
的检查,如果为 null
,才进入同步代码块。在同步代码块内部,再次检查 instance
是否为 null
,如果是则创建实例。这样可以大大减少对 synchronized
的使用,提高性能。
java
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
完整代码:
java
class SingletonLazy{
private static volatile SingletonLazy instance = null;
private static Object locker = new Object();
public static SingletonLazy getInstance(){
if(instance==null){
synchronized(locker){
if(instance==null){
instance = new SingletonLazy();
}
}
}
return instance;
}
private SingletonLazy(){}
}
public class test1 {
public static void main(String[] args) {
SingletonLazy s1 = SingletonLazy.getInstance();
SingletonLazy s2 = SingletonLazy.getInstance();
System.out.println(s1 == s2);
}
}
返回的结果是true为什么呢:
在单例模式中,SingletonLazy
类有一个私有的构造方法,以防止外部直接创建实例。取而代之的是,我们需要通过 getInstance()
方法来获取该类的单例实例。
当我们第一次调用 SingletonLazy.getInstance()
时,方法内部会检查 instance
变量是否为 null
。如果是 null
,则会创建一个新的 SingletonLazy
实例,并将其赋值给 instance
变量。
在第二次调用 SingletonLazy.getInstance()
时,由于 instance
变量已经不为 null
了,所以方法会直接返回之前创建的那个实例。
因此,s1
和 s2
变量都引用了同一个 SingletonLazy
实例,这是单例模式的设计目标。
所以,这段代码只会创建一个 SingletonLazy
实例,而不是两个。s1
和 s2
变量最终都指向同一个实例。
作者最开始理解,两个对象指向的引用不同为啥会返回true呢,问了AI就知道为啥了。上面就是原因。
总结:
在线程不安全的单例模式实现中,确实可能会创建两个以上的对象实例。这是因为在并发环境下,多个线程可能同时检查 instance
变量是否为 null
,并且同时创建新的实例。
让我们来看一下不同情况下会发生什么:
-
线程安全的单例模式:
- 第一次调用
getInstance()
时,会创建一个新的实例并赋值给instance
变量。 - 后续调用
getInstance()
时,由于instance
变量已经不为null
,直接返回之前创建的实例。 - 因此,只会创建一个实例。
- 第一次调用
-
线程不安全的单例模式:
- 多个线程同时进入
getInstance()
方法,并且都检查到instance
变量为null
。 - 这些线程会同时创建新的实例,导致最终存在多个实例。
- 这违背了单例模式的设计目标。
- 多个线程同时进入
所以,在线程不安全的情况下,可能会创建两个或更多的对象实例,这就破坏了单例模式的特性。
这就是为什么在实现单例模式时,我们需要特别注意线程安全的问题。通过同步、双重检查锁等技术,我们可以确保只有一个实例被创建,从而保证单例模式的正确性。