懒汉单例、双重校验锁、禁止指令重排序确保线程安全测试实例

This commit is contained in:
2018-09-24 13:08:48 +08:00
parent 90bbe74f2a
commit f975b3c9fe
2 changed files with 106 additions and 0 deletions

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}