Java中的DCL单例详解 volatile

在java中,有很多设计模式

单例模式:
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。


public class Test {
 private static final Test INSTANCE = new Test();
 
 private Test() {};
 
 public static Test getInstance() {
  return INSTANCE;
 }
 
 public static void main(String[] args) {
  Test test1 = Test.getInstance();
  Test test2 = Test.getInstance();
 }
}

通过一个INSTANCE 来保存这样一个单例,使得全局只有一个这个类的实例化对象

但是,这种方法有一些问题,当INSTANCE 所代表的类很大,那么对程序来说,如果INSTANCE并没有用到,那么就会造成资源的浪费,所以说就有了第一种优化的方法,当调用 Test.getInstance() 的时候,把这个方法变成这样:


public class Test {
 private static Test INSTANCE;
 
 private Test() {};
 
 public static Test getInstance() {
  if(INSTANCE==null) {
   INSTANCE = new Test();
  }
  return INSTANCE;
 }
 
 public static void main(String[] args) {
  Test test1 = Test.getInstance();
  Test test2 = Test.getInstance();
 }
}

此时,出现了另一个问题,当同时调用很多次这个getInstance方法,并发情况很大,如果第一个线程访问到了判断INSTANCEnull时候,第二个线程同样也进入了程序,并且在第一个线程并没有进行 INSTANCE = new Test();的时候,也判断了INSTANCEnull,那么,就会有两个INSTANCE 被初始化

此时,我们可以使用synchronized 来进行加锁,让只有一个线程来进行访问,不允许并发,那么代码就修改为这样:


public class Test {
 private static Test INSTANCE;
 
 private Test() {};
 
 public static Test getInstance() {
  if(INSTANCE==null) {
   synchronized(Test.class) {
    if(INSTANCE==null) {
     INSTANCE = new Test();
    }
   }
  }
  return INSTANCE;
 }
 
 public static void main(String[] args) {
  Test test1 = Test.getInstance();
  Test test2 = Test.getInstance();
 }
}

我们注意到,使用了两次判断 if(INSTANCE==null) ,主要原因是因为避免当第一个程序进行了访问,进行加锁的时候,第二个程序也进行访问,等待解锁,当第一个程序进行初始化Test后,第二个程序解锁又一次进入这个方法进行了一个初始化。所以加入了两次if判断,也就是:DCL(double check lock)

volatile有两个作用,保持线程可见性,禁止指令重排序,需要注意的是,当我们使用DCL时候,要定义INSTANCE为volatile,

private static volatile Test INSTANCE;

原因:
new 过程为:

  1. 申请内存空间(半初始化)
  2. 调用构造方法
  3. 建立关联(Date d= new Date() d与new Date()建立联系)

当没有加volatile ,有时候会进行指令重排序,如果调用构造方法和建立关联如果发生指令重排序,那么就会先建立关联,在调用构造方法。表面上可能没有问题,但是,如果在建立关联,调用构造方法之前(构造方法没有执行),第二个线程进行访问,此时INSTANCE不为空,但由于没有调用构造方法,成员变量没有赋值,则就会出现隐患。

—学习于b站 马士兵说 的美团面试七连问

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值