|
|
|
@ -16,36 +16,35 @@
|
|
|
|
|
* 双检锁 懒汉式,实现线程安全的单例
|
|
|
|
|
* 关键词:JVM指令重排、volatile、反射攻击
|
|
|
|
|
*/
|
|
|
|
|
public class Singleton3 {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 对于我们初级开发来说,这个volatile在实际开发中可能见过,但很少会用到
|
|
|
|
|
* 这里加个volatile进行修饰,也是本单例模式的精髓所在。
|
|
|
|
|
* 下面的 instance = new Singleton3(); 这行代码在JVM中其实是分三步执行的:
|
|
|
|
|
* 1、分配内存空间;
|
|
|
|
|
* 2、初始化对象;
|
|
|
|
|
* 3、将instance指向分配的内存地址。
|
|
|
|
|
* 但JVM具有指令重排的特性,实际的执行顺序可能会是1、3、2,导致多线程情况下出问题,
|
|
|
|
|
* 使用volatile修饰instance变量 可以 避免上述的指令重排
|
|
|
|
|
* tips:不太理解的是 第一个线程在执行第2步之前就已经释放了锁?导致其它线程进入synchronized代码块
|
|
|
|
|
* 执行 instance == null 的判断?
|
|
|
|
|
*/
|
|
|
|
|
private volatile static Singleton3 instance;
|
|
|
|
|
|
|
|
|
|
private Singleton3(){
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Singleton3 getInstance(){
|
|
|
|
|
if(instance == null){
|
|
|
|
|
synchronized(Singleton3.class){
|
|
|
|
|
if(instance == null){
|
|
|
|
|
instance = new Singleton3();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return instance;
|
|
|
|
|
}
|
|
|
|
|
public class Singleton3 {
|
|
|
|
|
/**
|
|
|
|
|
* 对于我们初级开发来说,这个volatile在实际开发中可能见过,但很少会用到
|
|
|
|
|
* 这里加个volatile进行修饰,也是本单例模式的精髓所在。
|
|
|
|
|
* 下面的 instance = new Singleton3(); 这行代码在JVM中其实是分三步执行的:
|
|
|
|
|
* 1、分配内存空间;
|
|
|
|
|
* 2、初始化对象;
|
|
|
|
|
* 3、将instance指向分配的内存地址。
|
|
|
|
|
* 但JVM具有指令重排的特性,实际的执行顺序可能会是1、3、2,导致多线程情况下出问题,
|
|
|
|
|
* 使用volatile修饰instance变量 可以 避免上述的指令重排
|
|
|
|
|
* tips:不太理解的是 第一个线程在执行第2步之前就已经释放了锁?导致其它线程进入synchronized代码块
|
|
|
|
|
* 执行 instance == null 的判断?
|
|
|
|
|
*/
|
|
|
|
|
private volatile static Singleton3 instance;
|
|
|
|
|
|
|
|
|
|
private Singleton3(){
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Singleton3 getInstance(){
|
|
|
|
|
if(instance == null){
|
|
|
|
|
synchronized(Singleton3.class){
|
|
|
|
|
if(instance == null){
|
|
|
|
|
instance = new Singleton3();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return instance;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
**2. 枚举实现**
|
|
|
|
|