新闻资讯

新闻资讯 行业动态

服务端性能优化之双重检查锁

编辑:008     时间:2020-02-18

单例锁的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进行同步的替代方法。


郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

回复列表

相关推荐