前言
单例模式是 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) {
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() 方法时加载。并且静态内部类是线程安全的。
为什么静态内部类是线程安全的
静态内部类使用类加载机制的初始化阶段方法。静态内部类的静态变量赋值操作其实是一个方法。执行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;
}
}
总结
暂无评论内容