《 Java 并发实战》中遇到的一个问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
labilixin
V2EX    Java

《 Java 并发实战》中遇到的一个问题

  •  
  •   labilixin 2023-08-02 16:52:51 +08:00 2742 次点击
    这是一个创建于 876 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在《 java 并发实战》中 3.4.2 使用 volatile 发布不可变对象 章节中有一段代码理解不了,希望大家能不能给一个更通俗的解释。

     @Immutable class OneValueCache { private final BigInteger lastNumber; private final BigInteger[] lastFactos; public OneValueCache(BigInteger i, BigInteger[] factors) { lastNumber = i; lastFactors = Arrays.copyOf(factors, factors.length); } public BigInteger[] getFactors(BigInteger i) { if (lastNumber == null || !lastNumber.equals(i)) return null; else return Arrays.copyOf(lastFactors, lastFactors.length); } } public class VolatileCachedFactorizer implements Servlet { private volatile OneValueCache cache = new OneValueCache(null, null); public void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req); BigInteger[] factors = cache.getFactors(i); if (factors == null) { factors = factor(i); cache = new OneValueCache(i, factors); // 这一行看起来不像是线程安全。 } encodeIntoResponse(resp, factors); } } 

    这里原文解释说 VolatileCacheFactorizer 是线程安全的。但是我看起来 cache = new OneValueCache(i, factors)这行是线程不安全的。

    虽然 OneValueCache 是不可变对象,但是 VolatileCachedFactorizer 里的 cache 是有可能被多个线程同时写入的吧?

    希望大佬能帮忙解答一下,谢谢了。

    11 条回复    2023-08-04 16:49:15 +08:00
    Leo666666
        1
    Leo666666  
       2023-08-02 18:14:58 +08:00
    这里的 cache 是被 volatile 修饰的,当一个变量被 volatile 修饰时,它具有以下特性:
    1. 可见性:对一个 volatile 变量的写操作能立即被其他线程所看到,而其他线程的读操作也能得到最新值。
    2. 有序性:加入 volatile 修饰的变量在编译器和 CPU 层面会进行指令重排的优化,保证 volatile 变量之前的操作不会被编译器和 CPU 重排到 volatile 变量之后。
    QiWa
        2
    QiWa  
       2023-08-02 18:19:48 +08:00
    volatile 可以看成一个轻量 synchronized
    zhzy0077
        3
    zhzy0077  
       2023-08-02 18:45:46 +08:00 via Android
    变量赋值是原子的 多个线程同时写入最后一个为准
    mango88
        4
    mango88  
       2023-08-02 19:53:20 +08:00   1
    cache = new OneValueCache(i, factors) 写了之后,由于有 volatile 存在 另外一个线程执行 cache.getFactors(i) 立马可见
    你提到的 `cache 是有可能被多个线程多个线程同时写入的`
    是有可能的,
    但是从他的业务来看,虽然是不同的 cache 对象,但是不会引起业务错误,这应该是他想表达“线程安全”的意思
    lingalonely
        5
    lingalonely  
       2023-08-02 20:11:20 +08:00
    halozzz
        6
    halozzz  
       2023-08-02 20:12:44 +08:00 via iPhone
    注意 OneValueCache 里的操作都是 copy 的,不是直接赋值和返回值,这里的线程安全是指这个类两次初始化数据不会互相干扰,而不是只会初始化一次。
    halozzz
        7
    halozzz  
       2023-08-02 20:20:39 +08:00 via iPhone
    @lingalonely
    Nothing guarantees that two threads won't compute the factors from the same number. The only guarantee that this code offers is that, if the cache currently contains the factors for the requested number, these cached factors will be returned (and not factors for another number, or inconsistent data cause by a data race)
    race condition 依旧存在,不过能保证的是返回的值不会因为 race 存在而有不一样的结果
    Nerv
        8
    Nerv  
       2023-08-02 20:22:18 +08:00
    1. 引用写入具备原子性
    2. volatile 写入使引用具备可见性,OneValueCache 的属性是 final 的,final 的特殊规则说明其初始化之后对所有线程可见,因此属性访问也满足可见性,其总体满足可见性
    3. 即使被多个线程同时写入,导致的后果也就是多创建了几次对象(多余的对象会被 gc ),cache 存储的是最后一次被写入的值,不对后续 cache 的使用造成影响,满足一致性。
    lifespy
        9
    lifespy  
       2023-08-03 13:41:04 +08:00
    @Leo666666 #1 在 Java 中,使用 volatile 修饰的变量可以防止指令重排序。
    labilixin
        10
    labilixin  
    OP
       2023-08-03 15:36:04 +08:00
    感谢各位的解答,我最后的理解是,其实这个不是线程安全的。只不过他返回的值跟 cache 没关系。所以最后的结果是线程安全的。像 8 楼 @Nerv 说的一样,我也比较倾向于这种解释。

    感觉这个这本书里这个例子举的有点迷惑性。如果他是返回值跟 cache 有关系的话其实感觉会有可能返回意料之外的结果的。
    ZiChun
        11
    ZiChun  
       2023-08-04 16:49:15 +08:00
    ChatGPT 如是说:
    在这段代码中,虽然看起来 cache = new OneValueCache(i, factors) 这一行在没有同步机制的情况下,似乎是线程不安全的,但实际上由于 volatile 关键字和 OneValueCache 对象的不可变性,使得 VolatileCachedFactorizer 在多线程环境中仍然是线程安全的。即使多个线程试图同时创建新的 OneValueCache 对象并赋值给 cache ,但因为 volatile 关键字的内存可见性特性,所有线程都会读取到最新的 cache 值。另外,由于 OneValueCache 对象的不变性,即使 cache 被新的 OneValueCache 对象替换,其他线程获取的旧 OneValueCache 对象仍然是一致和有效的,因此并不会影响程序的正确性。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3215 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 11:12 PVG 19:12 LAX 03:12 JFK 06:12
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86