美文网首页
“Java 并发编程实战” 3.5 节 安全发布疑问

“Java 并发编程实战” 3.5 节 安全发布疑问

作者: tingshuo123 | 来源:发表于2020-10-05 16:34 被阅读0次
    // 不安全的发布
    public Holder holder;
    public void initialize() {
        holder = new Holder(42);
    } 
    
    public class Holder {
        private int n;
    
        public Holder(int n) {
            this.n = n;
        }
        
        public void assertSanity() {
            if (n != n) {
                throw new AssertionError("This statement is false.");
            }
        }
    }
    

    这是在 3.5 节中有一段非常反直觉的代码,大意是说如果采用上面的初始化方式,另一个线程调用 assertSanity 方法时可能会抛 AssertionError,不知道时水平不够还是翻译有问题, 这段我想了久没有想明白,后来在 stack overflow 上找到了对这段代码的解释。 https://stackoverflow.com/questions/1621435/not-thread-safe-object-publishing

    The reason why this is possible is that Java has a weak memory model. It does not guarantee ordering of read and writes.

    This particular problem can be reproduced with the following two code snippets representing two threads.

    Thread 1:

    someStaticVariable = new Holder(42);
    

    Thread 2:

    someStaticVariable.assertSanity(); // can throw
    

    On the surface it seems impossible that this could ever occur. In order to understand why this can happen, you have to get past the Java syntax and get down to a much lower level. If you look at the code for thread 1, it can essentially be broken down into a series of memory writes and allocations:

    1. Alloc memory to pointer1
    2. Write 42 to pointer1 at offset 0
    3. Write pointer1 to someStaticVariable

    Because Java has a weak memory model, it is perfectly possible for the code to actually execute in the following order from the perspective of thread 2:

    1. Alloc Memory to pointer1
    2. Write pointer1 to someStaticVariable
    3. Write 42 to pointer1 at offset 0

    Scary? Yes but it can happen.

    What this means though is that thread 2 can now call into assertSanity before n has gotten the value 42. It is possible for the value n to be read twice during assertSanity, once before operation #3 completes and once after and hence see two different values and throw an exception.

    EDIT

    According to Jon Skeet, the AssertionError migh still occur with Java 8 unless the field is final.

    出现这种情况是因为 JVM 的重排序引起的,这句初始化代码 someStaticVariable = new Holder(42); 可以分为三步 1. 申请内存 2. 对 n 赋值 3. 把引用赋值给 someStaticVariable。因为重排序可能打乱这三不的循序。可能会出现,已经把引用赋值 someStaticVariable 变量了,但是 n 还没有赋值的情况,而 assertSanity 中的一个 n 读的是赋值前的数据,一个是读赋值后的数据所以不相等。

    相关文章

      网友评论

          本文标题:“Java 并发编程实战” 3.5 节 安全发布疑问

          本文链接:https://www.haomeiwen.com/subject/qguauktx.html