在 java 中注入/访问线程安全单例的最佳方法



我对这段代码完全感到困惑,因为我知道其目的是让线程安全访问对象 DataService 的单个实例,但我不知道它是否实现了。

它对我来说很突出调用方类:

protected final DataService dataService = DataService.getInstance();

单例类方法:

private static DataService dService=null;
public static synchronized DataService getInstance() {
    if (dService == null)
        dService = new DataService();
    return dService;
}

在调用类中使用受保护的 final 修饰符可以实现什么?

它们是否必要且是一种良好做法?

第一行上的受保护修饰符和最终修饰符对您所指的单例性质没有任何贡献。

受保护只是访问修饰符,final 确定是否可以重新分配变量。

根据单例的定义,真正使它成为单例的是,您只创建一个dService对象(通过new DataService()),并且由于该方法是同步的,因此可以保证该方法不会同时调用,这可能导致错误地创建两个单独的对象。

正如 Brian Benzinger 所说,受保护修饰符和最终修饰符对单例类的线程安全性没有贡献。 受保护定义了 Caller 类中字段的可见性,即该字段可以被包中的其他类访问。最后一个修饰符指示字段必须在实例化时明确赋值,以后不能重新赋值。

尽管有很多方法可以在 Java 中实现单例模式,但这是我所知道的最好的实现:

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar(); 
            }
        }
        return bar;
    }
}

所以我最终挖了自己。

我发现这种做单例的方式效率低下,称为

双重检查锁定。

"在软件工程中,双重检查锁定(也称为"双重检查锁定优化")是一种软件设计模式,用于通过首先测试锁定标准("锁定提示")而不实际获取锁来减少获取锁的开销。仅当锁定条件检查指示需要锁定时,才会继续执行实际的锁定逻辑。""由于在某些极端情况下,同步方法可能会将性能降低 100 倍或更高"

Java 1.5+ 中提到的最佳方法是使用易失性。

class Foo {
    private volatile Helper helper;
    public Helper getHelper() {
        Helper result = helper;
        if (result == null) {
            synchronized(this) {
                result = helper;
                if (result == null) {
                    helper = result = new Helper();
                }
            }
        }
        return result;
    }
    // other functions and members...
}

"易失性字段仅访问一次(由于"返回结果"而不是"返回帮助程序;"),这可以将方法的整体性能提高多达 25%"

最新更新