关于java:4Volatile的应用

1次阅读

共计 1707 个字符,预计需要花费 5 分钟才能阅读完成。

单例模式

单线程下的单例模式代码(懒汉,实用于单线程)

public class SingletonDemo {
    // 用动态变量保留这个惟一实例
    private static SingletonDemo instance = null;
    
    // 结构器私有化
    private SingletonDemo() {}
    
    // 提供一个静态方法来获取实例对象
    public static SingletonDemo getInstance() {if (instance == null) {instance = new SingletonDemo();
        }
        return instance;
    }
}

单线程下创立进去的都是同一个对象。然而在多线程的环境下,咱们的单例模式是否还是同一个对象了?

public class SingletonDemo {
    // 用动态变量保留这个惟一实例
    private static SingletonDemo instance = null;

    // 结构器私有化
    private SingletonDemo() {System.out.println(Thread.currentThread().getName() + "\t 我是构造方法");
    }

    // 提供一个静态方法来获取实例对象
    public static SingletonDemo getInstance() {if (instance == null) {instance = new SingletonDemo();
        }
        return instance;
    }

    // 测试多线程是否是同一个实例!public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(() -> {SingletonDemo.getInstance();
            }, "线程" + i).start();}
    }
}
  • 从上面的后果咱们能够看出,咱们通过 SingletonDemo.getInstance() 获取到的对象,并不是同一个,而是被上面几个线程都进行了创立,那么在多线程环境下,单例模式如何保障呢?

解决办法 1

引入 synchronized 关键字

public synchronized static SingletonDemo getInstance() {if(instance == null) {instance = new SingletonDemo();
    }
    return instance;
}

然而 synchronizaed 属于重量级的同步机制,它只容许一个线程同时拜访获取实例的办法,然而因而减低了并发性,因而采纳的比拟少。

解决办法 2

通过引入 DCL Double Check Lock 双端检锁机制

就是在进来和进来的时候,进行检测

public static SingletonDemo getInstance() {if (instance == null) {synchronized (SingletonDemo.class) {if (instance == null) {instance = new SingletonDemo();
            }
        }
    }
    return instance;
}

DCL 的确可能保障单例模式的正确性,然而下面的办法还是存在问题的。

DCL(双端检锁)机制不肯定是线程平安的,起因是有 指令重排 的存在,退出 volatile 能够禁止指令重排

private static volatile SingletonDemo instance = null;

因为 instance 能够分为三局部进行实现:

  • memory = allocate() // 1、调配对象内存空间
  • instance(memory) // 2、初始化对象
  • instance = memory // 3、设置 instance 指向刚刚调配的内存地址,此时instance != null

失常来说执行完 2 语句再执行 3 语句,对象初始化后,咱们才指向内存地址。

然而执行完 1 语句后,步骤 2 和 步骤 3 之间不存在 数据依赖关系,而且无论重排前 还是重排后,程序的执行后果在单线程中并没有扭转,因而这种重排优化是容许的。

这样就会造成什么问题呢?

咱们先执行步骤 2 时,因为对象的初始化还没有实现,所以试图获取 instance 时,会失去 null。因而执行单例模式的代码时候,就会从新再创立一个 instance 实例。

正文完
 0