简介
早前的文章中讨论过服务端性能优化之异步查询转同步,在本文中,将讨论双重检查锁定设计模式。通过简单地事先检查锁定条件,该模式减少了锁定获取的次数,通常可以提高性能。
下面是之前关于锁和线程安全的文章的一些文章:
单例锁的 Demo
首先,让我们看一个具有严格同步的简单单例:
public class DraconianSingleton {
private static DraconianSingleton instance;
public static synchronized DraconianSingleton getInstance() {
if (instance == null) {
instance = new DraconianSingleton();
}
return instance;
}
// 其他方法
}
尽管此类是线程安全的,但我们可以看到仍然存在明显的性能缺陷:每次我们想要获取单例的实例时,我们都需要获取可能不必要的锁。
要解决此问题,我们可以首先验证是否需要首先创建对象,只有在这种情况下,我们才能获取锁。
更进一步,我们想在进入同步块后立即再次执行相同的检查,以保持原子操作:
public class DclSingleton {
private static volatile DclSingleton instance;
public static DclSingleton getInstance() {
if (instance == null) {
synchronized (DclSingleton.class) {
if (instance == null) {
instance = new DclSingleton();
}
}
}
return instance;
}
// 其他方法
}
使用此模式要牢记的一件事是,该字段必须是volatile
,以防止缓存不一致性问题。实际上,Java 内存模型允许发布仅仅部分初始化的对象,而这又可能导致其他 BUG。
替代方案
即使经过双重检查的锁定可能会加快速度,但它至少存在两个问题:
- 由于它要求 volatile 关键字才能正常工作,因此它与 Java 1.4 及更低版本不兼容
- 它很冗长,使代码难以阅读
由于这些原因,让我们研究没有这些缺陷的其他方案。以下所有方法都将同步任务交给JVM
。
早期初始化
实现线程安全的最简单方法是内联对象创建或使用等效的静态块。这利用了以下事实:静态字段和块一个接一个地初始化(Java语言规范12.4.2)
:
public class EarlyInitSingleton {
private static final EarlyInitSingleton INSTANCE = new EarlyInitSingleton();
public static EarlyInitSingleton getInstance() {
return INSTANCE;
}
// 其他方法
}
按需初始化
另外,由于从上一段的 Java 语言规范参考中知道,类初始化是在我们第一次使用其方法或字段之一时发生的,因此我们可以使用嵌套的静态类来实现延迟初始化:
public class InitOnDemandSingleton {
private static class InstanceHolder {
private static final InitOnDemandSingleton INSTANCE = new InitOnDemandSingleton();
}
public static InitOnDemandSingleton getInstance() {
return InstanceHolder.INSTANCE;
}
// 其他方法
}
在这种情况下,InstanceHolder
类将在我们第一次通过调用getInstance
来访问该字段时进行分配。
枚举
最后一个解决方案使用枚举而不是类。在本文时,这被认为是编写单例的最简洁,最安全的方法:
public enum EnumSingleton {
INSTANCE;
// 其他方法
}
总结
总而言之,这篇文章介绍了双重检查的锁定模式,它的限制和一些替代方法。在实践中,过多的冗长性和缺乏向后兼容性使此模式易于出错,因此我们应该避免这种情况。相反,我们应该考虑使用让JVM
进行同步的替代方法。
- 郑重声明:文章首发于公众号 “FunTester”,禁止第三方(腾讯云除外)转载、发表。
技术类文章精选
- Linux 性能监控软件 netdata 中文汉化版
- 性能测试框架第三版
- 如何在 Linux 命令行界面愉快进行性能测试
- 图解 HTTP 脑图
- 将 swagger 文档自动变成测试代码
- Selenium 4.0 Alpha 更新日志
- Selenium 4.0 Alpha 更新实践
- 如何统一接口测试的功能、自动化和性能测试用例