扬中本地网站,鄂州seo厂家,建筑企业资质新规定2022,华为服务器文章目录 定义单例模式的实现构成构成UML图 单例模式的六种实现懒汉式-线程不安全懒汉式-线程安全饿汉式-线程安全双重校验锁-线程安全静态内部类实现枚举实现 总结其他设计模式文章#xff1a;最后 定义 单例模式是一种创建型设计模式#xff0c;它用来保证一个类只有一个实… 文章目录 定义单例模式的实现构成构成UML图 单例模式的六种实现懒汉式-线程不安全懒汉式-线程安全饿汉式-线程安全双重校验锁-线程安全静态内部类实现枚举实现 总结其他设计模式文章最后 定义 单例模式是一种创建型设计模式它用来保证一个类只有一个实例 并且提供一个访问该实例的全局节点。其在很多场景中都有应用比如数据库连接池、日志记录器、Spring中对象的创建等。
总的来说单例模式在需要控制实例数量、确保全局唯一性的场景中被广泛应用。单例模式通过限制类的实例化对象为一个可以确保全局唯一性的场景中被广泛应用从而有助于控制资源访问、简化全局访问点、减少内存占用等在很多情况下都可以提升程序的运行效率。
单例模式的实现构成
构成
一个私有的构造函数、一个私有的静态变量以及一个共有的静态函数。
其中私有构造函数保证了其他线程不能通过new来创建对象实例而共有的静态函数则是用来后续所有对此函数的调用都返回唯一的私有静态变量。
UML图 单例模式的六种实现
懒汉式-线程不安全
下面实现中instance 被延迟实例化这样的话当没有使用到这个类的话就会节约资源不会实例化 LazySingletonsAreNotSafe。
但是该实现是线程不安全的因为在多线程环境下可以有多个线程同时进入 getInstance 方法并且这个时候 instance 还未实例化那么它们就都可以进入到 if 逻辑中执行实例化操作从而导致线程不安全问题。
public class LazySingletonsAreNotSafe {private static LazySingletonsAreNotSafe instance;private LazySingletonsAreNotSafe() {}public static LazySingletonsAreNotSafe getInstance() {if (instance null) {instance new LazySingletonsAreNotSafe();}return instance;}
}懒汉式-线程安全
那么如何可以保证线程安全呢
其实上一个实现方式中线程不安全就是因为 instance 的实例化被执行了很多次所以我们只要对 getInstance 方法进行加锁保证同一个时间点只有一个线程可以进入该方法进行实例化操作那么就保证了线程安全问题。 实现代码如下
public class LazySingletonsAreSafe {private static LazySingletonsAreSafe instance;private LazySingletonsAreSafe() {}// 关键点synchronized进行了加锁操作从而保证线程安全。public static synchronized LazySingletonsAreSafe getInstance() {if (instance null) {instance new LazySingletonsAreSafe();}return instance;}
}饿汉式-线程安全
对于懒汉式方法如果不加锁会导致线程安全问题而加锁虽然会保证线程安全但是也带来了一定程度上的性能损耗因此可以采用饿汉式。 懒汉式线程安全问题的原因是 getInstance 方法可能被执行多次从而导致被实例化多次。所以我们采用在类加载的时候直接实例化 instance 这样就会避免实例化多次的问题。 当然因为我们一开始在类加载的时候对象就被实例化了所以也不会有延迟实例化种可以节约资源的优点。 public class EagerSingleton {private static final EagerSingleton instance new EagerSingleton();private EagerSingleton() {}public static EagerSingleton getInstance() {return instance;}
}双重校验锁-线程安全
双重校验锁先判断 uniqueInstance 是否已经被实例化如果没有被实例化那么才对实例化语句进行加锁。
public class DoubleCheckedLockingSingleton {// 注意volatile 修饰private static volatile DoubleCheckedLockingSingleton instance;private DoubleCheckedLockingSingleton() {}public static DoubleCheckedLockingSingleton getInstance() {if (instance null) {synchronized (DoubleCheckedLockingSingleton.class) {if (instance null) {instance new DoubleCheckedLockingSingleton();}}}return instance;}
}问题1 为什么两个if?
if (instance null) {synchronized (DoubleCheckedLockingSingleton.class) {if (instance null) {instance new DoubleCheckedLockingSingleton();}}}第一个if是因为高并发场景下还是可能有不止一个线程成功的在 instance 还未初始化的时候就进入这里了所以他们都会走下面的逻辑所以加了一把锁用来保证线程安全问题。 而第二个if则是因为等到第一个线程执行完实例化之后它会释放锁这样的话下一个线程就会来拿这把锁然后进行新一轮的实例化。所以在锁里添加了第二个if用来进行判断避免实例化多次。
问题2 为什么 instance 用 volatile 进行修饰
private static volatile DoubleCheckedLockingSingleton instance;这个是因为 volatile 有禁止指令重排的功能。上述代码中单例对象有的时候可能会发生空指针异常的问题。
对于instance new DoubleCheckedLockingSingleton(); 它其实是分为三个步骤来执行的
JVM为对象分配内存在内存中进行对象的初始化将内存对应的地址复制给instance
假设现在有两个线程进入到了getInstance方法当T1线程执行实例化操作时T2线程在进行判断。
因为instance new DoubleCheckedLockingSingleton();操作不是原子的所以编译器可能会进行指令的重排序即
JVM为对象分配内存将内存对应的地址复制给instance在内存中进行对象的初始化
这样的话当T1线程执行完第二步地址复制给instance的时候T2线程去进行判断那么instance null则是为true所以会直接跳到最下面 return instance。从而导致空指针问题。
而volatile可以避免指令重排所以只要用volatile修饰instance就可以避免这个问题了。
静态内部类实现
当 BillPughSingleton 类加载时静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance 方法从而触发 SingletonHolder.INSTANCE时SingletonHolder才会被加载进行初始化。
public class BillPughSingleton {private BillPughSingleton() {}private static class SingletonHelper {private static final BillPughSingleton INSTANCE new BillPughSingleton();}public static BillPughSingleton getInstance() {return SingletonHelper.INSTANCE;}
}枚举实现
枚举实例的创建是线程安全的而且在任何情况下都是它一个单例。在别的几种单例中反序列化时会重新创建对象而枚举单例则不存在这种情况。
public enum EnumSingleton {INSTANCE;public void someMethod() {}
}总结 1. 饿汉式 实现在类加载时就完成了实例化。 特点线程安全实现简单但可能会造成资源浪费因为即使不需要使用实例也会在类加载时创建。 2. 懒汉式 实现在第一次调用 getInstance() 方法时进行实例化。 特点延迟加载节省资源但需要在 getInstance() 方法上加锁才可以保证线程安全会影响性能。 3. 双重校验锁 实现在 getInstance() 方法中加入两次实例检查第二次检查前加上锁既保证了线程安全又提高了效率。 特点结合了懒汉式和饿汉式的优点既实现了延迟加载又优化了并发性能。 4. 静态内部类 实现将单例实例放在静态内部类中当外部类被加载时静态内部类并不会被加载只有在首次调用 getInstance() 方法时才会加载。 特点既实现了延迟加载又保证了线程安全且不需显式同步。 5. 枚举 实现利用枚举类型的特性来保证实例的唯一性。 特点线程安全简洁易读还能防止反序列化攻击。
其他设计模式文章
设计模式 - Singleton pattern 单例模式 设计模式 - Factory Method 工厂方法 设计模式 - Chain Of Responsibility 责任链模式 设计模式 - Template Method 模板方法 设计模式 - Strategy Pattern策略模式 设计模式 - Observer Pattern 观察者模式
最后
如果小伙伴们觉得我写的文章不错的话那么请给我点点关注我们下次见