DCL: double check lock 双检锁
饿汉式
package leetcode0606._volatile;
/**
* 简单实用,推荐使用!
* 缺点:不管用到与否,类加载时都完成了实例化
* 饿汉式
*/
public class Test1 {
public static void main(String[] args) {
MyClass instance1 = MyClass.getInstance();
MyClass instance2 = MyClass.getInstance();
System.out.println(instance1==instance2); // true
}
}
class MyClass{
//
private static final MyClass INSTANCE = new MyClass();
private MyClass(){}
public static MyClass getInstance(){
return INSTANCE;
}
}
懒汉式
package leetcode0606._volatile;
/*
懒汉式
*/
public class Test2 {
public static void main(String[] args) {
}
}
class MyClass2{
private static MyClass2 INSTANCE;
private MyClass2() {
}
public static MyClass2 getINSTANCE() {
if(INSTANCE == null){
INSTANCE = new MyClass2();
}
return INSTANCE;
}
}
但是存在线程不安全的问题。导致拿不到同一个对象
优化第一步:粗粒度的锁
public static synchronized MyClass2 getINSTANCE() {
if(INSTANCE == null){
INSTANCE = new MyClass2();
}
return INSTANCE;
}
}
第二步:细粒度的锁
public static /*synchronized*/ MyClass2 getINSTANCE() {
// 业务代码
//......
//......
if(INSTANCE == null){
synchronized (MyClass2.class){
INSTANCE = new MyClass2();
}
}
return INSTANCE;
}
}
但是有不安全的问题,两个线程都停在if之后
第三步:DCL
public static /*synchronized*/ MyClass2 getINSTANCE() {
// 业务代码
//......
//......
if(INSTANCE == null){ // 这一行可去掉,但是会影响效率
synchronized (MyClass2.class){
if(INSTANCE == null){
INSTANCE = new MyClass2();
}
}
}
return INSTANCE;
}
会有指令重构排序问题,导致数据不一致。
第四步:DCL+volatile
private static volatile MyClass2 INSTANCE;
为什么要加??
解释:
第一个线程来了
发生了指令重排序
此时第二个线程来了,发现它不为空,那我就直接拿来用了,结果就拿到了半初始化的对象。
综上,饿汉式没有线程问题,类加载时,直接加载静态变量,JVM保证了这一点。
但是懒汉式有线程安全问题和指令重排序的问题。
但是更加底层一点是怎么实现的呢?
在JVM层面上,被volatile修饰的内存不可以被指令重排序
内存屏障(JVM层面上的逻辑概念,CPU底层实现不知道)保障了两条指令不可以挪位置!即在中间加一堵墙,过不去!!
Load 读
Store 写
LoadLoad 内存屏障 : 两条读指令不可以换位置
具体实现:如果你想往volatile修饰的内存里写 如果你想读