From c982cd4a7fecc2fb07135ccfa2c6ed2b5955f470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=E5=98=89=E9=98=B3?= Date: Mon, 24 Sep 2018 13:43:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=87=92=E6=B1=89=E5=8D=95=E4=BE=8B=E3=80=81?= =?UTF-8?q?=E9=9D=99=E6=80=81=E5=86=85=E9=83=A8=E7=B1=BB=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E5=AE=89=E5=85=A8=E6=B5=8B=E8=AF=95=E5=AE=9E?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../singleton/LazyDoubleCheckSingleton.java | 25 ++++--- .../singleton/StaticInnerClassSingleton.java | 65 +++++++++++++++++++ .../LazyDoubleCheckSingletonTest.java | 4 +- .../StaticInnerClassSingletonTest.java | 35 ++++++++++ 4 files changed, 117 insertions(+), 12 deletions(-) create mode 100644 src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/StaticInnerClassSingleton.java create mode 100644 src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/StaticInnerClassSingletonTest.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 index 3956e2a..2b7acee 100644 --- a/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazyDoubleCheckSingleton.java +++ b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazyDoubleCheckSingleton.java @@ -33,15 +33,17 @@ public class LazyDoubleCheckSingleton { public static LazyDoubleCheckSingleton getInstance() { // 首次检查 if (lazySingleton == null) { - // 再次检查,在此处加锁比对方法加锁开销更小,因为如果首次检查不为空则直接返回 synchronized (LazyDoubleCheckSingleton.class) { - log.debug("LazySingletonSynchronized实例化"); - lazySingleton = new LazyDoubleCheckSingleton(); - // 实例化包含三个过程,这三个过程可能由于指令重排序导致执行顺序打乱 - // 但Java底层只保证三个过程在单线程内顺序一致 - // 1.分配内存 - // 2.初始化 - // 3.LazyDoubleCheckSingleton对象指向刚分配的内存地址 + // 再次检查,在此处加锁比对方法加锁开销更小,因为如果首次检查不为空则直接返回 + if (lazySingleton == null) { + log.debug("LazyDoubleCheckSingleton实例化"); + lazySingleton = new LazyDoubleCheckSingleton(); + // 实例化包含三个过程,这三个过程可能由于指令重排序导致执行顺序打乱 + // 但Java底层只保证三个过程在单线程内顺序一致 + // 1.分配内存 + // 2.初始化 + // 3.LazyDoubleCheckSingleton对象指向刚分配的内存地址 + } } } return lazySingleton; @@ -57,8 +59,11 @@ public class LazyDoubleCheckSingleton { // 使用线程等待模拟复杂实例化过程,让多线程同时进入该方法 Thread.sleep(1000); synchronized (LazyDoubleCheckSingleton.class) { - log.debug("LazySingletonSynchronized实例化"); - lazySingleton = new LazyDoubleCheckSingleton(); + // 再次检查,在此处加锁比对方法加锁开销更小,因为如果首次检查不为空则直接返回 + if (lazySingleton == null) { + log.debug("LazyDoubleCheckSingleton实例化"); + lazySingleton = new LazyDoubleCheckSingleton(); + } } } return lazySingleton; diff --git a/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/StaticInnerClassSingleton.java b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/StaticInnerClassSingleton.java new file mode 100644 index 0000000..6001b8f --- /dev/null +++ b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/StaticInnerClassSingleton.java @@ -0,0 +1,65 @@ +package top.fjy8018.designpattern.pattern.creational.singleton; + + +import lombok.extern.slf4j.Slf4j; + +/** + * 懒汉单例:静态内部类实现(线程安全) + * 使用静态内部类可以屏蔽类实例化的重排序,因为非构造线程不允许看到指令重排序 + * + * @author F嘉阳 + * @date 2018-09-24 13:11 + */ +@Slf4j +public class StaticInnerClassSingleton { + /** + * 私有构造器是单例必须的 + */ + private StaticInnerClassSingleton() { + } + + /** + * 开发单例入口 + * + * @return + */ + public static StaticInnerClassSingleton getInstance() { + return InnerClass.staticInnerClassSingleton; + } + + /** + * 静态内部类 + *

+ * 原理: + * JVM在类初始化时会加锁,使用初始化锁可以同步多个线程对一个类的初始化,确保了线程安全 + * 类初始化时机 + * 一. 主动引用 + * 虚拟机规范中并没有强制约束何时进行加载,但是规范严格规定了有且只有下列五种情况必须对类进行初始化(加载、验证、准备都会随之发生): + * 1. 遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类没有进行过初始化,则必须先触发其初始化。 + * 最常见的生成这 4 条指令的场景是: + * (1) 使用 new 关键字实例化对象的时候; + * (2) 读取或赋值一个类的静态字段(被 final 修饰、已在编译期把结果放入常量池的静态字段除外)的时候; + * (3) 以及调用一个类的静态方法的时候。 + * (4) 使用一个类的非常量静态成员 + * (5) 若一个类是顶级类,且该类有嵌套的断言语句(少见) + *

+ * 2. 使用 {@link java.lang.reflect} 包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先触发其初始化。 + * 3. 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。 + * 4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含 main() 方法的那个类),虚拟机会先初始化这个主类; + * 5. 当使用 JDK 1.7 的动态语言支持时,如果一个 {@link java.lang.invoke.MethodHandle} + * 实例最后的解析结果为 REF_getStatic, REF_putStatic, REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化; + *

+ * 二. 被动引用 + * 以上 5 种场景中的行为称为对一个类进行主动引用。除此之外,所有引用类的方式都不会触发初始化,称为被动引用。被动引用的常见例子包括: + *

+ * 1. 通过子类引用父类的静态字段,不会导致子类初始化。 + * System.out.println(SubClass.value); // value 字段在 SuperClass 中定义 + * 2. 通过数组定义来引用类,不会触发此类的初始化。该过程会对数组类进行初始化,数组类是一个由虚拟机自动生成的、直接继承自 {@link Object} 的子类,其中包含了数组的属性和方法。 + * SuperClass[] sca = new SuperClass[10]; + * 3. 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。 + * System.out.println(ConstClass.HELLOWORLD); + */ + private static class InnerClass { + private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton(); + } +} 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 index 21690f5..76db35e 100644 --- a/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/LazyDoubleCheckSingletonTest.java +++ b/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/LazyDoubleCheckSingletonTest.java @@ -28,9 +28,9 @@ class LazyDoubleCheckSingletonTest { private class MyRunnable implements Runnable { @Override public void run() { - LazySingletonSynchronized lazySingleton = null; + LazyDoubleCheckSingleton lazySingleton = null; try { - lazySingleton = LazySingletonSynchronized.getInstance2(); + lazySingleton = LazyDoubleCheckSingleton.getInstance2(); } catch (InterruptedException e) { e.printStackTrace(); } diff --git a/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/StaticInnerClassSingletonTest.java b/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/StaticInnerClassSingletonTest.java new file mode 100644 index 0000000..bba6ac5 --- /dev/null +++ b/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/StaticInnerClassSingletonTest.java @@ -0,0 +1,35 @@ +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 StaticInnerClassSingletonTest { + + @Test + void getInstance() throws InterruptedException { + Thread thread1 = new Thread(new StaticInnerClassSingletonTest.MyRunnable()); + Thread thread2 = new Thread(new StaticInnerClassSingletonTest.MyRunnable()); + + thread1.start(); + thread2.start(); + + Thread.sleep(500); + log.debug("finish"); + } + + private class MyRunnable implements Runnable { + @Override + public void run() { + StaticInnerClassSingleton lazySingleton = null; + try { + lazySingleton = StaticInnerClassSingleton.getInstance(); + } catch (Exception e) { + e.printStackTrace(); + } + log.info(Thread.currentThread().getName() + " " + lazySingleton); + } + } +} \ No newline at end of file