Effective Java 第 83 条的示例代码是不是错的?

330 天前
 Bronya

关于多线程的,书中给了一个延迟初始化的例子,用的是双重检查方式:

// Double-check idiom for lazy initialization of instance fields
private volatile FieldType field;

private FieldType getField() {
    FieldType result = field;
    if (result == null) { // First check (no locking)
        synchronized (this) {
            if (field == null) // Second check (with locking)
                field = result = computeFieldType();
        }
    }
    return result;
}

这里是原书第三版的第 83 条:

这里面的局部变量 result 不是很好理解,而且我在 JDK 21 下跑出来是可能取到 null 的,求大佬解释一下~

破案了,就是错的,看这里https://github.com/jbloch/effective-java-3e-source-code/issues/8,issues 里面给出了解决方法:

private FieldType getField() {
	FieldType result = field;
	if (result == null) { // First check (no locking)
		synchronized (this) {
			if (field == null) // Second check (with locking)
				field = result = computeFieldValue();
			else
				result = field;
		}
	}
	return result;
}
2011 次点击
所在节点    Java
7 条回复
beneo
330 天前
这个语法我都没见过了

field = result = computeFieldType();
cover
330 天前
自问自答?
cover
330 天前
就是两个线程一起 synchronized 锁的时候,field 初始化成功了,但是 result 没值,后进 synchronized 那根线程拿到了 null ,修复方案也显而易见,就是让后进的线程也拿到已经被前置线程赋值完成的 field ,其实我觉得最后返回 field 更加方便。
Bronya
330 天前
@cover #2 刚开始没找到那个 ussues ,发完帖子之后又搜索了一下,发现原来真的是作者搞错了,然后赶紧编辑了一下,看起来就像自问自答了😂
flython6
330 天前
这个例子确实是错的返回 result
但是我纠结的是为什么要使用局部变量……解释感觉跟没说的一样
flython6
330 天前
@cover 哦我懂作者意思了,确实 else 直接返回 field 也可以,还省了一步赋值操作
但是最外面返回 result 主要是想要避免 volatile 变量读取时的缓存行失效,这样可以提升性能(着实是没什么用处的提升)
如果 DCL 最外层检测失败,或者修改后代码没进入 else ,也可以确保最少限度地访问 field (因为每次访问 volitale 都会使缓存行失效从而从主内存加载最新变量副本到工作缓存)

我理解应该是这个意思,欢迎指正
kneo
330 天前
别用这种方法,古老而易错。在我看来这是一个 anti pattern ,应该从 effective java 里拿掉。
搜索一下 enum singleton 。或者,就用一个普通的锁。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1004173

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX