懒汉单例、静态内部类确保线程安全测试实例
This commit is contained in:
@@ -33,15 +33,17 @@ public class LazyDoubleCheckSingleton {
|
|||||||
public static LazyDoubleCheckSingleton getInstance() {
|
public static LazyDoubleCheckSingleton getInstance() {
|
||||||
// 首次检查
|
// 首次检查
|
||||||
if (lazySingleton == null) {
|
if (lazySingleton == null) {
|
||||||
// 再次检查,在此处加锁比对方法加锁开销更小,因为如果首次检查不为空则直接返回
|
|
||||||
synchronized (LazyDoubleCheckSingleton.class) {
|
synchronized (LazyDoubleCheckSingleton.class) {
|
||||||
log.debug("LazySingletonSynchronized实例化");
|
// 再次检查,在此处加锁比对方法加锁开销更小,因为如果首次检查不为空则直接返回
|
||||||
lazySingleton = new LazyDoubleCheckSingleton();
|
if (lazySingleton == null) {
|
||||||
// 实例化包含三个过程,这三个过程可能由于指令重排序导致执行顺序打乱
|
log.debug("LazyDoubleCheckSingleton实例化");
|
||||||
// 但Java底层只保证三个过程在单线程内顺序一致
|
lazySingleton = new LazyDoubleCheckSingleton();
|
||||||
// 1.分配内存
|
// 实例化包含三个过程,这三个过程可能由于指令重排序导致执行顺序打乱
|
||||||
// 2.初始化
|
// 但Java底层只保证三个过程在单线程内顺序一致
|
||||||
// 3.LazyDoubleCheckSingleton对象指向刚分配的内存地址
|
// 1.分配内存
|
||||||
|
// 2.初始化
|
||||||
|
// 3.LazyDoubleCheckSingleton对象指向刚分配的内存地址
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lazySingleton;
|
return lazySingleton;
|
||||||
@@ -57,8 +59,11 @@ public class LazyDoubleCheckSingleton {
|
|||||||
// 使用线程等待模拟复杂实例化过程,让多线程同时进入该方法
|
// 使用线程等待模拟复杂实例化过程,让多线程同时进入该方法
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
synchronized (LazyDoubleCheckSingleton.class) {
|
synchronized (LazyDoubleCheckSingleton.class) {
|
||||||
log.debug("LazySingletonSynchronized实例化");
|
// 再次检查,在此处加锁比对方法加锁开销更小,因为如果首次检查不为空则直接返回
|
||||||
lazySingleton = new LazyDoubleCheckSingleton();
|
if (lazySingleton == null) {
|
||||||
|
log.debug("LazyDoubleCheckSingleton实例化");
|
||||||
|
lazySingleton = new LazyDoubleCheckSingleton();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lazySingleton;
|
return lazySingleton;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 静态内部类
|
||||||
|
* <p>
|
||||||
|
* 原理:
|
||||||
|
* <strong>JVM在类初始化时会加锁,使用初始化锁可以同步多个线程对一个类的初始化,确保了线程安全</strong>
|
||||||
|
* 类初始化时机
|
||||||
|
* 一. 主动引用
|
||||||
|
* 虚拟机规范中并没有强制约束何时进行加载,但是规范严格规定了有且只有下列五种情况必须对类进行初始化(加载、验证、准备都会随之发生):
|
||||||
|
* 1. 遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类没有进行过初始化,则必须先触发其初始化。
|
||||||
|
* 最常见的生成这 4 条指令的场景是:
|
||||||
|
* (1) 使用 new 关键字实例化对象的时候;
|
||||||
|
* (2) 读取或赋值一个类的静态字段(被 final 修饰、已在编译期把结果放入常量池的静态字段除外)的时候;
|
||||||
|
* (3) 以及调用一个类的静态方法的时候。
|
||||||
|
* (4) 使用一个类的非常量静态成员
|
||||||
|
* (5) 若一个类是顶级类,且该类有嵌套的断言语句(少见)
|
||||||
|
* <p>
|
||||||
|
* 2. 使用 {@link java.lang.reflect} 包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先触发其初始化。
|
||||||
|
* 3. 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
|
||||||
|
* 4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含 main() 方法的那个类),虚拟机会先初始化这个主类;
|
||||||
|
* 5. 当使用 JDK 1.7 的动态语言支持时,如果一个 {@link java.lang.invoke.MethodHandle}
|
||||||
|
* 实例最后的解析结果为 REF_getStatic, REF_putStatic, REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化;
|
||||||
|
* <p>
|
||||||
|
* 二. 被动引用
|
||||||
|
* 以上 5 种场景中的行为称为对一个类进行主动引用。除此之外,所有引用类的方式都不会触发初始化,称为被动引用。被动引用的常见例子包括:
|
||||||
|
* <p>
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,9 +28,9 @@ class LazyDoubleCheckSingletonTest {
|
|||||||
private class MyRunnable implements Runnable {
|
private class MyRunnable implements Runnable {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
LazySingletonSynchronized lazySingleton = null;
|
LazyDoubleCheckSingleton lazySingleton = null;
|
||||||
try {
|
try {
|
||||||
lazySingleton = LazySingletonSynchronized.getInstance2();
|
lazySingleton = LazyDoubleCheckSingleton.getInstance2();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user