JAVA双重锁的线程安全问题及解决方案

探讨Java单例模式中的双重检查锁定(DCL)机制在多线程环境下存在的线程安全问题,分析JVM指令重排序的影响,并提供两种解决方案:使用volatile关键字和内部静态类实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java单例双重锁的线程安全问题,附解决方案
想必大家都写过单例模式,双重锁几乎是大家用到最多的写法如下所示。
public class Singleton {
  private static Hello instance;
  private Singleton() {}
  public static Hello getInstance(){
    if (instance == null) {
      synchronized (Singleton.class) {
        if (instance == null) {
          instance = new Hello();
        }
      }
    }
    return instance;
  }
}

上面这段代码中instance = new Hello();这句话在单线程中是没有问题的,然而在多线程中是有安全问题的。
因为这涉及jvm指令重排序的问题,例如这段instance = new Hello();在内存中是如何执行的:
一种可能是:
a. 分配一块内存空间
b. 在内存中初始化一个Hello实例
c. 将声明的instance引用指向这个内存
另一种可能是:
a. 分配一块内存空间
b. 将声明的instance引用指向这个内存
c. 在内存中初始化一个Hello实例
针对下面这种可能,假设有两个线程A和B,A线程刚好执行到instance = new Hello();此时instance指向了一块内存空间,但此时该内存还没有进行实例化,B线程执行if (instance == null) 操作,此时instance已经指向了内存空间,instance已经不为null了,B线程就直接返回了instance实例,这时就出现问题了,因为B线程拿到了一个不完成的instance实例,这就是导致线程不安全的原因。

解决这个问题我总结了两个方案:

方案一
在成员变量加上volatile字段,其他代码不变,这就能解决指令重排的问题。
例如

  private volatile static Hello instance;

方案二 使用内部静态类实现getInstance方法

public class Singleton {
  public  static Hello getInstance(){
    return InnerSingleton.instance;
  }
  private static class InnerSingleton{
    private static Hello instance = new Hello();
  }
  }

内部类InnerSingleton只有在getInstance第一次调用的时候才会被加载,实现了延迟加载的效果,而且加载过程是线程安全的实现了线程安全问题,内部类加载的时候只会实例化一次instance。

总结

个人建议使用内部类实现单例模式,希望对使用单例的小伙伴们有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值