V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Bronya
V2EX  ›  Java

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

  •  
  •   Bronya · 2023-12-28 17:03:16 +08:00 · 2050 次点击
    这是一个创建于 365 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    // 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;
    }
    
    7 条回复    2023-12-28 21:44:06 +08:00
    beneo
        1
    beneo  
       2023-12-28 17:07:39 +08:00
    这个语法我都没见过了

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

    我理解应该是这个意思,欢迎指正
    kneo
        7
    kneo  
       364 天前 via Android
    别用这种方法,古老而易错。在我看来这是一个 anti pattern ,应该从 effective java 里拿掉。
    搜索一下 enum singleton 。或者,就用一个普通的锁。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2897 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 11:21 · PVG 19:21 · LAX 03:21 · JFK 06:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.