美文网首页
多线程Debug窥探单例模式

多线程Debug窥探单例模式

作者: pepsi1000 | 来源:发表于2019-08-07 17:33 被阅读0次

1. 懒汉式单例模式

通过延迟初始化,降低单例创建期间的资源开销。

懒汉式单例实现,存在线程安全问题

public class LazySingleton {
    private static LazySingleton lazySingleton;
    private LazySingleton() {}
    public static LazySingleton getInstance() {
        if (lazySingleton == null) {  // 断点,suspend:thread
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}

线程任务

@Slf4j
public class T implements Runnable {
    @Override
    public void run() {
        LazySingleton instance = LazySingleton.getInstance();
        log.info("{} - {}", Thread.currentThread(), instance);
    }
}

在主线程中创建两个线程任务T,通过IDEA多线程Debug控制线程的执行顺序,使其在控制台输出不同的实例。

private void singleton() {
    Thread t1 = new Thread(new T());
    Thread t2 = new Thread(new T());
    t1.start();
    t2.start();
    log.info("end"); // 断点,suspend:thread
}

备注:IDEA如何实现多线程Debug

断点-右键断点-挂起选择thread模式

1.1. 使用synchronized解决懒汉式线程安全问题

public synchronized static LazySingleton getInstance() {
    if (lazySingleton == null) {
        lazySingleton = new LazySingleton();
    }
    return lazySingleton;
}

这时候在使用多线程Debug,可以发现其中一个线程任务处于阻塞状态。

这种方法性能开销较大。

备注:synchronized锁静态方法相当于锁整个类文件,synchronized锁普通方法相当于锁堆内存中的对象实例。

1.2. 使用double check双重检查解决懒汉式线程安全问题

public class LazyDoubleCheckSingleton {
    private static LazyDoubleCheckSingleton lazyDoubleCheckSingleton;
    private LazyDoubleCheckSingleton() {}
    public static LazyDoubleCheckSingleton getInstance() {
        if (lazyDoubleCheckSingleton == null) {
            synchronized (LazyDoubleCheckSingleton.class) {
                if (lazyDoubleCheckSingleton == null) {
                    lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
                }
            }
        }
        return lazyDoubleCheckSingleton;
    }
}

存在指令重排序问题,即lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();这行代码实际上执行了三个步骤:

  1. 分配堆内存
  2. 初始化对象(类加载之后,被线程使用之前)
  3. 将栈中的变量指向堆内存

2和3的执行顺序可能颠倒,这就是指令重排序,在单线程环境下这个问题不会影响到程序正常执行,但是在多线程环境下,线程1跳过了步骤2执行了步骤3,这时候线程1挂起,CPU控制权切换到线程2,线程2拿到还没初始化的对象,程序就会抛异常。

解决指令重排序问题:

1)不允许指令重排序。使用volatile

private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton;

通过volatile配合double check双重检查的懒汉式单例模式不仅解决了线程安全问题,而且还兼顾到了性能,这种方式会比直接使用synchronized来得更好点。

2)允许指令重排序,但其它线程不可见,通过静态内部类解决。

public class StaticInnerClassSingleton {
    private StaticInnerClassSingleton() {}
    private static class InnerClass {
        private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }
    public static StaticInnerClassSingleton getInstance() {
        return InnerClass.staticInnerClassSingleton;
    }
}

Class对象的初始化锁,即哪个线程拿到InnerClass对象的初始化锁,哪个对象就去初始化它,而其它线程属于非构造线程,所以即使构造期间出现了指令重排序,那么其它线程也是看不见的。

2. 饿汉式单例模式

使用final static修饰实例对象使类在加载完成之后,完成实例对象的初始化,且不能再修改。

public class HungrySingleton {
    private final static HungrySingleton hungrySingleton;
    static {
        hungrySingleton = new HungrySingleton();
    }
    private HungrySingleton() {}
    public static HungrySingleton getInstance() {
        return hungrySingleton;
    }
}

这种方法使类加载完成之后就完成实例对象的初始化,若应用里没有使用到这个单例对象就会造成内存资源的浪费。与懒汉式不同,懒汉式是需要使用时才去初始化对象,所以不用考虑资源浪费问题。但是懒汉式存在线程安全问题,而饿汉式则没这种问题,开发者可以根据实际的业务需求选择最恰当的解决方案。

3. 破坏单例模式

序列化反序列化会破坏单例模式:

序列化和反序列化时获取到的是两个不同的实例对象,因为反序列化时,会判断单例类中是否存在readReslove方法,如果有的话会调用这个方法返回单例对象,否则会通过反射机制重新获取一个新的单例对象。

反射攻击也会破坏单例模式。

使用枚举实现单例,1)枚举单例类不受序列化反序列化破坏,2)屏蔽反射攻击

相关文章

  • 多线程Debug窥探单例模式

    1. 懒汉式单例模式 通过延迟初始化,降低单例创建期间的资源开销。 懒汉式单例实现,存在线程安全问题 线程任务 在...

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • Java多线程--并行模式与算法

    Java多线程--并行模式与算法 单例模式 虽然单例模式和并行没有直接关系,但是我们经常会在多线程中使用到单例。单...

  • 单例模式

    单例模式介绍 把单例模式放到多线程基础这块,是因为单例和多线程有一点的关系。何为单例模式? 在它的核心结构中只包含...

  • Unity3d游戏开发之-单例设计模式-多线程一

    单例模式3:多线程一

  • 单例模式

    单例模式 单例模式:用来保证一个对象只能被创建一次。 普通版 代码实现如下 同步锁单例 单例模式如果再多线程中使用...

  • 多线程(下)&GUI

    day25(多线程(下)&GUI) 1_多线程(单例设计模式)(掌握) 单例设计模式:保证类在内存中只有一个对象。...

  • 设计模式——单例模式的破坏

    概述: 之前学习了单例模式的几种实现,解决了多线程情况下,单例的线程安全问题,保证了单例的实现。但是单例模式在下面...

  • iOS 多线程NSThread,GCD,NSOperation

    单例模式例子: https://github.com/XiaoRuiZuo/Singleton 多线程:多线程是为...

  • day25-多线程/Timer/单例模式/工厂模式/GUI

    25.01_单例设计模式(掌握) 单例设计模式:保证类在内存中只有一个对象。 25.02_多线程(Runtime类...

网友评论

      本文标题:多线程Debug窥探单例模式

      本文链接:https://www.haomeiwen.com/subject/xajadctx.html