懒汉单例、双重校验锁、禁止指令重排序确保线程安全测试实例
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