Java 并发系列(二):DCL — Double Check Lock

本文探讨了DCL(Double Check Lock)在Java多线程环境中的目的,存在的问题及其解决方案,包括JDK1.3以后的volatile使用,以及DCL的替代方案——延迟初始化占位类模式。DCL由于历史问题已被弃用,现代并发优化更倾向于使用更易理解和高效的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. DCL 的目的

Double Check Lock 是多线程环境下为提高延迟初始化效率而被广泛使用的一种方式。我们常常会使用延迟初始化,以降低服务启动时间。

/**
 * code 1.1
 */
@NotThreadSafe
public class Client {
  private LazyInitClass instance ;
  public LazyInitClass getInstance() {
    if(instance == null)
      instance = new LazyInitClass("LazyInitClassFieldName") ;
    return instance ;
  }
}

代码

上面的代码是典型的延迟初始化的例子。当上面的例子暴露在多线程环境下时,便会出现各种问题。最明显的错误:方法会返回多个 LazyInitClass 对象。

/**
 * code 1.2
 */
@NotThreadSafe
public class Client {
	private LazyInitClass instance ;

	public synchronized LazyInitClass getInstance() {
		if(instance == null)
			instance = new LazyInitClass("LazyInitClassFieldName") ;

		return instance ;
	}
}

代码

上面的代码在方法层面使用了 synchronized 关键字,每次调用 getInstance 方法都进行同步,的确可以有效避免多线程环境下多次调用 getInstance 得到不同的 LazyInitClass 对象。但当 instance 初始化完成后,同步便没有了意义。同步则成为影响 getInstance 性能的关键。有没有一种方法,可以在初始化时进行正确的同步,初始化完成后又避免同步呢?于是 DCL 出现了。

/**
 * code 1
### 解决Java/C# 泛型单例模式下多线程安全问题的最佳实践 #### Java中的双重检查锁定(DCL) 为了确保泛型单例模式在多线程环境下的正确工作,在Java中可以采用双重检查锁定(Double Checked Locking, DCL)的方式。这种方式通过减少锁的竞争来提高性能。 ```java public class Singleton<T> { private volatile T instance; public T getInstance() { if (instance == null) { // First check synchronized (Singleton.class) { if (instance == null) { // Second check instance = createInstance(); } } } return instance; } protected T createInstance() { // 实现创建实例逻辑 return null; } } ``` 上述代码利用`volatile`关键字防止指令重排序带来的问题,保证了即使是在高并发场景下单例对象也能被正确初始化[^1]。 #### C# 中基于静态构造函数的方法 对于C#, 可以依赖于CLR所提供的特性——即当首次访问某个类型的任何成员之前都会自动执行该类型的静态构造器,并且此操作是完全由运行库管理并具有内在的线程安全性。 ```csharp public sealed class Singleton<T> { private static readonly Lazy<Dictionary<Type, object>> instances = new Lazy<Dictionary<Type, object>>(() => new Dictionary<Type, object>()); private static readonly object syncRoot = new object(); private Singleton() {} public static T Instance { get { var type = typeof(T); lock (syncRoot) { if (!instances.Value.ContainsKey(type)) { instances.Value[type] = Activator.CreateInstance(type); } return (T)instances.Value[type]; } } } } ``` 这段代码展示了如何使用`Lazy<T>`配合字典存储不同类型的单例实例,同时借助`lock`语句块保护对共享资源的操作,从而实现了线程安全的泛型单例模式[^2]。 另外一种更简洁的做法就是直接定义一个私有的静态只读字段用于保存唯一的实例: ```csharp public class Singleton<T> where T : class, new(){ private static readonly T _instance = new T(); public static T Instance => _instance; } ``` 这里的关键在于.NET框架本身已经确保了类级别的初始化过程是线程安全的,因此不需要额外加锁就能达到目的[^3]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值