懒汉单例、双重校验锁、禁止指令重排序确保线程安全测试实例
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
package top.fjy8018.designpattern.pattern.creational.singleton;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 懒汉式单例:延迟加载(双重校验锁保证线程安全)
|
||||
*
|
||||
* @author F嘉阳
|
||||
* @date 2018-09-24 10:29
|
||||
*/
|
||||
@Slf4j
|
||||
public class LazyDoubleCheckSingleton {
|
||||
/**
|
||||
* volatile确保内存可见性,同时禁止指令重排
|
||||
* volatile会增加一定的汇编代码:
|
||||
* 1. 将当前处理器缓存行的数据写回系统内存
|
||||
* 2. 写回系统内存后使其他处理器缓存的地址无效,迫使其他处理器从共享内存中同步最新数据
|
||||
*/
|
||||
private volatile static LazyDoubleCheckSingleton lazySingleton = null;
|
||||
|
||||
/**
|
||||
* 单例模式构造器必须是屏蔽的
|
||||
*/
|
||||
private LazyDoubleCheckSingleton() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 双重校验锁确保线程安全
|
||||
* 存在指令重排序导致的隐患
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static LazyDoubleCheckSingleton getInstance() {
|
||||
// 首次检查
|
||||
if (lazySingleton == null) {
|
||||
// 再次检查,在此处加锁比对方法加锁开销更小,因为如果首次检查不为空则直接返回
|
||||
synchronized (LazyDoubleCheckSingleton.class) {
|
||||
log.debug("LazySingletonSynchronized实例化");
|
||||
lazySingleton = new LazyDoubleCheckSingleton();
|
||||
// 实例化包含三个过程,这三个过程可能由于指令重排序导致执行顺序打乱
|
||||
// 但Java底层只保证三个过程在单线程内顺序一致
|
||||
// 1.分配内存
|
||||
// 2.初始化
|
||||
// 3.LazyDoubleCheckSingleton对象指向刚分配的内存地址
|
||||
}
|
||||
}
|
||||
return lazySingleton;
|
||||
}
|
||||
|
||||
/**
|
||||
* 线程安全验证
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static LazyDoubleCheckSingleton getInstance2() throws InterruptedException {
|
||||
if (lazySingleton == null) {
|
||||
// 使用线程等待模拟复杂实例化过程,让多线程同时进入该方法
|
||||
Thread.sleep(1000);
|
||||
synchronized (LazyDoubleCheckSingleton.class) {
|
||||
log.debug("LazySingletonSynchronized实例化");
|
||||
lazySingleton = new LazyDoubleCheckSingleton();
|
||||
}
|
||||
}
|
||||
return lazySingleton;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package top.fjy8018.designpattern.pattern.creational.singleton;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@Slf4j
|
||||
class LazyDoubleCheckSingletonTest {
|
||||
|
||||
@Test
|
||||
void getInstance() {
|
||||
LazyDoubleCheckSingleton instance = LazyDoubleCheckSingleton.getInstance();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInstance2() throws InterruptedException {
|
||||
Thread thread1 = new Thread(new LazyDoubleCheckSingletonTest.MyRunnable());
|
||||
Thread thread2 = new Thread(new LazyDoubleCheckSingletonTest.MyRunnable());
|
||||
|
||||
thread1.start();
|
||||
thread2.start();
|
||||
|
||||
Thread.sleep(3000);
|
||||
log.debug("finish");
|
||||
}
|
||||
|
||||
private class MyRunnable implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
LazySingletonSynchronized lazySingleton = null;
|
||||
try {
|
||||
lazySingleton = LazySingletonSynchronized.getInstance2();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
log.info(Thread.currentThread().getName() + " " + lazySingleton);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user