Java中最简单的设计模式之一是否多线程安全(组图)

前言

单例模式是 Java 中最简单的设计模式之一。这种类型的设计模式是一种创建模式,它提供了一种创建对象的最佳方式。

此模式涉及一个类,该类负责创建自己的对象,同时确保只创建一个对象。此类提供了一种直接访问其唯一对象的方法,无需实例化该类的对象。

饥饿单箱

线程是否安全:是

是否延迟加载:否

顾名思义,Hungry 需要直接创建实例。

public class EhSingleton {
    private static EhSingleton ehSingleton = new EhSingleton();
    private EhSingleton() {}
    public static EhSingleton getInstance(){
        return ehSingleton;
    }
}

缺点:类加载初始化,浪费内存

优点:无锁,执行效率高。或者一个线程安全的实例。

懒惰的单身人士

惰性单例,类初始化时不创建实例,只有调用时才创建实例。

非线程安全的惰性单例

是否多线程安全:否

是否延迟加载:是

public class LazySingleton {
    private static LazySingleton ehSingleton;
    private LazySingleton() {}
    public static LazySingleton getInstance() {
        if (ehSingleton == null) {
            ehSingleton = new LazySingleton();
        }
        return ehSingleton;

    }
}

调用getInstance后才会创建实例,优点是不占用内存,在单线程模式下是安全的。但是在多线程模式下,如果(ehSingleton == null) 被多个线程同时执行,结果都为真,会创建多个实例,所以上面的惰性单例是线程不安全的实例。

带有同步锁的惰性单例

线程是否安全:是

是否延迟加载:是

为了解决多个线程同时执行if(ehSingleton == null)的问题,getInstance方法增加了同步锁,保证一个线程进入getInstance方法,其他线程无法进入该方法,只有执行完成后,其他线程才能进入方法,同时只能有一个线程进入方法。

public class LazySingletonSync {
    private static LazySingletonSync lazySingletonSync;
    private LazySingletonSync() {}
    public static synchronized LazySingletonSync getInstance() {
        if (lazySingletonSync == null) {

图片[1]-Java中最简单的设计模式之一是否多线程安全(组图)-唐朝资源网

lazySingletonSync =new LazySingletonSync(); } return lazySingletonSync; } }

这种配置虽然保证了线程的安全,但是效率低下。只有第一次调用初始化后才需要同步,初始化后不需要同步。锁的粒度过大,影响程序的执行效率。

双重测试惰性单例

线程是否安全:是

是否延迟加载:是

使用synchronized声明的方法,当多个线程访问时,比如A线程,其他线程必须等待A线程执行完毕才能访问,大大降低了程序的运行效率。这时候使用同步代码块来优化执行时间,减少锁的粒度。

先复查,判断实例是否为空,然后使用synchronized(LazySingletonDoubleCheck.class)使用类锁来锁定整个类。执行完代码块的代码后,会创建一个新的实例,其他代码不走if(lazySingletonDoubleCheck = = null),只会一开始比较慢。在synchronized中需要判断,因为可能有多个线程同时执行synchronized(LazySingletonDoubleCheck.class)。如果一个线程创建了一个新实例,其他线程如果不为空就可以得到lazySingletonDoubleCheck,不会再创建实例了。 .

public class LazySingletonDoubleCheck {
    private static LazySingletonDoubleCheck lazySingletonDoubleCheck;
    private LazySingletonDoubleCheck() {}

    public static LazySingletonDoubleCheck getInstance() {
        if (lazySingletonDoubleCheck == null) {
            synchronized (LazySingletonDoubleCheck.class) {
                if (lazySingletonDoubleCheck == null) {
                    lazySingletonDoubleCheck = new LazySingletonDoubleCheck();
                }
            }
        }
        return lazySingletonDoubleCheck;
    }
}

静态内部类

线程是否安全:是

是否延迟加载:是

加载外部类时,不会加载内部类,也不执行new SingletonHolder(),属于懒加载。 SingletonHolder 类仅在第一次调用 getInstance() 方法时加载。并且静态内部类是线程安全的。

为什么静态内部类是线程安全的

图片[2]-Java中最简单的设计模式之一是否多线程安全(组图)-唐朝资源网

静态内部类使用类加载机制的初始化阶段方法。静态内部类的静态变量赋值操作其实是一个方法。执行getInstance()方法时,虚拟机会加载SingletonHolder静态内部类。

p>

然后加载静态内部类,内部类有静态变量,JVM会改变内部生成方法,然后在初始化时执行该方法——即执行静态变量的赋值。

虚拟机保证方法在多线程环境下使用锁同步,只执行一次方法。

这种方法不仅实现了延迟加载,还保证了线程安全。

public class StaticClass {
    private StaticClass() {}
    private static class SingletonHolder {
        private static final SingletonHolder INSTANCE = new SingletonHolder();
    }
    public static final SingletonHolder getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

总结

© 版权声明
THE END
喜欢就支持一下吧
点赞282 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片