Java 双锁 syn 为什么不加 volatile 就是非绝对安全的? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
monetto
V2EX    Java

Java 双锁 syn 为什么不加 volatile 就是非绝对安全的?

  •  
  •   monetto 2019-08-23 18:08:36 +08:00 4383 次点击
    这是一个创建于 2298 天前的主题,其中的信息可能已经有所发展或是发生改变。

    网上有人说是因为指令重排序

    锁{ 1. 申请内存 2. 初始化为一个 Object 3. 将指针指向 Object }

    但是执行是按照 1 3 2 执行的 造成没初始化完毕线程 2 进入 syn 锁内, 然后判断 null 成功, 又初始化了一个对象

    但是我理解即使是按照 1 3 2 执行的,JVM 不也应该执行完全部代码才能释放 syn 吗?也就是说,2 过程没执行完毕呢这个锁也不可能被释放掉啊。

    我个人感觉是为 第一次检查的时候, 线程 2 缓存了这个对象, 然后因为没加 volatile, 第二次判断是否为 null 仍然是从其 CPU 缓存里面检查的, 加 volatile 是为了保证其每次都重新去内存里面取一下然后做判断。

    麻烦大神解答一下,小弟感激不尽

    Leiothrix
        1
    Leiothrix  
       2019-08-23 18:17:40 +08:00
    指令重排,保证命令的原子性
    sudden
        2
    sudden  
       2019-08-23 19:03:10 +08:00
    public class Singleton {
    private static Singleton uniqueSingleton;

    private Singleton() {
    }

    public Singleton getInstance() {
    if (null == uniqueSingleton) {
    synchronized (Singleton.class) {
    if (null == uniqueSingleton) {
    uniqueSingleton = new Singleton(); // error
    }
    }
    }
    return uniqueSingleton;
    }
    }

    第一次 null 检查并没有在 syn 只内,所以线程 2 会访问到线程 1 未初始化完全的对象。
    Duluku
        3
    Duluku  
       2019-08-23 19:11:42 +08:00 via iPhone
    老哥,是这样如果是 1 额我这个顺序、那么 3 执行完之后、这个 instance 就不是 null 了、那么这样在这一瞬间就有可能被别的线程取走这个 instance、但是这个 instance 还是空的、会爆 npe

    public static Test getInstance() {
    if (instance == null) {
    synchronized (Test.class) {
    if (instance == null) {
    instance = new Test();
    }
    所以这个 instance 必须 volatile
    lurenw
        4
    lurenw  
       2019-08-23 19:14:01 +08:00
    多年前看到一个解释(中文博客看到的,可能不正确)

    DCL 安全问题的根源是, 初始化内存后 Object 就不为 null, 但是 Object 中的 Field 仍旧未被分配值. 此时其他线程就会判断 Object != null. 那么后续拿到的 field 就是未分配值的 field.

    加了 volatile 之后, 就会 lock 住这个变量所在的缓存(可能 lock 总线, 也可能 lock cache line), 导致其他 cpu 不能访问. 需要等到更新 wirte 完毕, 才能读取.
    Duluku
        5
    Duluku  
       2019-08-23 19:19:21 +08:00 via iPhone
    @lurenw 将某个对象声明为 volatile 之后、在这个对象被修改之后会立即写回、并将其他线程中所获得的该对象缓存全部宣布失效、强制所有的其他线程重新获取新的数值
    jieee
        6
    jieee  
       2019-08-23 19:21:22 +08:00
    volatile 保证可见性
    Raymon111111
        7
    Raymon111111  
       2019-08-23 19:29:47 +08:00
    可以看这个 https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

    主要还是指令重排的问题
    lurenw
        8
    lurenw  
       2019-08-23 19:47:13 +08:00
    @Duluku emmm... 你是要纠正我么, 你说的这个只是表现, 底层就是声言 LOCK, 锁 cache line 或 锁 bus.
    monetto
        9
    monetto  
    OP
       2019-08-23 20:14:58 +08:00
    @sudden 仅有当执行完 1 2 3 之后才会释放掉 Syn 不是吗?没初始化完成的话,线程 2 怎么进入 Syn 内的呢?
    monetto
        10
    monetto  
    OP
       2019-08-23 20:16:02 +08:00
    @lurenw 难道 new instance 这个过程没有被完全执行完毕,Syn 就会被释放掉吗?
    momocraft
        11
    momocraft  
       2019-08-23 20:27:04 +08:00
    在不正同步你看到象引用 (!= null) 不保任何事

    Java Concurrency in Practice 本唯一一次出 "infamous" 就是模式什
    Kahnn
        12
    Kahnn  
       2019-08-23 20:39:10 +08:00
    前 3 楼说的对
    @monetto 没初始化完成的话,线程 2 怎么进入 Syn 内的呢?没初始化的话线程 2 不需要进入 Sync 里啊,直接 return uniqueSingleton 了
    mreasonyang
        13
    mreasonyang  
       2019-08-24 03:37:38 +08:00 via iPhone   1
    @monetto 对象本身的内存已经分配,此时对象就不是 null 了,所以在没有用 volatile 禁止指令重排时,其他线程走到第一个 if 判断后将认为对象不为空而不进入 sync 逻辑进而直接获取到对象。但此时对象的属性等数据可能还没初始化完成,这就会导致操作对象属性等数据时出现 NPE 或不符合预期的数据
    IamNotShady
        14
    IamNotShady  
       2019-08-24 11:48:39 +08:00
    学习了
    monetto
        15
    monetto  
    OP
       2019-08-24 15:28:06 +08:00
    @Kahnn
    @mreasonyang
    @momocraft
    谢谢大家,懂了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1226 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 16:59 PVG 00:59 LAX 08:59 JFK 11:59
    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