diff --git a/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazyDoubleCheckSingleton.java b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazyDoubleCheckSingleton.java new file mode 100644 index 0000000..3956e2a --- /dev/null +++ b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazyDoubleCheckSingleton.java @@ -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; + } +} diff --git a/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/LazyDoubleCheckSingletonTest.java b/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/LazyDoubleCheckSingletonTest.java new file mode 100644 index 0000000..21690f5 --- /dev/null +++ b/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/LazyDoubleCheckSingletonTest.java @@ -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); + } + } +} \ No newline at end of file