From f975b3c9fe0a8c214c7ff0b214ffb025cbb3183e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=E5=98=89=E9=98=B3?= Date: Mon, 24 Sep 2018 13:08:48 +0800 Subject: [PATCH] =?UTF-8?q?=E6=87=92=E6=B1=89=E5=8D=95=E4=BE=8B=E3=80=81?= =?UTF-8?q?=E5=8F=8C=E9=87=8D=E6=A0=A1=E9=AA=8C=E9=94=81=E3=80=81=E7=A6=81?= =?UTF-8?q?=E6=AD=A2=E6=8C=87=E4=BB=A4=E9=87=8D=E6=8E=92=E5=BA=8F=E7=A1=AE?= =?UTF-8?q?=E4=BF=9D=E7=BA=BF=E7=A8=8B=E5=AE=89=E5=85=A8=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=AE=9E=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../singleton/LazyDoubleCheckSingleton.java | 66 +++++++++++++++++++ .../LazyDoubleCheckSingletonTest.java | 40 +++++++++++ 2 files changed, 106 insertions(+) create mode 100644 src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazyDoubleCheckSingleton.java create mode 100644 src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/LazyDoubleCheckSingletonTest.java 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