英文:
Why JMM produces (0, 0) even though it is considered a forbidden result
问题
我正在检查一些来自JMM(Java内存模型)的语句,并且我编写了一个类似这样的JCS(Java Concurrency Stress)测试:
    @JCStressTest
    @State
    @Outcome(expect = ACCEPTABLE, desc = "ACCEPTABLE")
    public class ConcurrencyTest {
        private final int a = 1;
        private final int b = 2;
    
        public ConcurrencyTest instance;
        
        @Actor
        public void actor1() {
            instance = new ConcurrencyTest();
        }
    
        @Actor
        public void actor2(II_Result result) {
            ConcurrencyTest c = instance;
            if (c != null) {
                result.r1 = c.a;
                result.r2 = c.b;
            }
        }
    }
在运行了这个测试之后,我看到了以下结果:
> (0, 0) (1, 2)
尽管JMM明确指出结果(0, 0)是不允许的,但是为什么会发生这种情况呢?
英文:
I am checking some statements from JMM and I wrote a JCS test like this:
@JCStressTest
@State
@Outcome(expect = ACCEPTABLE,  desc = "ACCEPTABLE")
public class ConcurrencyTest {
    private final int a = 1;
    private final int b = 2;
    public ConcurrencyTest instance;
    
    @Actor
    public void actor1() {
        instance = new ConcurrencyTest();
    }
    @Actor
    public void actor2(II_Result result) {
        ConcurrencyTest c = instance;
        if (c != null) {
            result.r1 = c.a;
            result.r2 = c.b;
        }
    }
}
After running this test, I see the following results:
> (0, 0) (1, 2)
Although the JMM explicitly states that the result (0, 0) is forbidden, why is this happening?
答案1
得分: 2
让我们首先稍微修改一下代码:
@JCStressTest
@State
@Outcome(id = "0, 0", expect = Expect.FORBIDDEN)
@Outcome(id = "1, 2", expect = Expect.ACCEPTABLE)
@Outcome(id = "-1, -1", expect = Expect.ACCEPTABLE)
public class ConcurrencyTest {
    private final int a = 1;
    private final int b = 2;
    public ConcurrencyTest instance;
    @Actor
    public void actor1() {
        instance = new ConcurrencyTest();
    }
    @Actor
    public void actor2(II_Result result) {
        ConcurrencyTest c = instance;
        if (c != null) {
            result.r1 = c.a;
            result.r2 = c.b;
        } else {                   // <-- 这是你需要关注的部分
            result.r1 = -1;
            result.r2 = -1;
        }
    }
}
你认为@Outcome(id = "0, 0")中的值是从哪里来的?这些值是你在II_Result中设置的,它保存了两个int值,默认值为0。
因此,当c == null(表示actor1尚未运行)时,if (c != null) { ... }将不会被执行。所以,在你的代码中,你不会做任何操作:导致r1和r2的默认值为零。你应该通过一个简单的else来处理这种默认情况,就像我做的一样。
英文:
Let's change the code a little bit to begin with:
@JCStressTest
@State
@Outcome(id = "0, 0", expect = Expect.FORBIDDEN)
@Outcome(id = "1, 2", expect = Expect.ACCEPTABLE)
@Outcome(id = "-1, -1", expect = Expect.ACCEPTABLE)
public class ConcurrencyTest {
    private final int a = 1;
    private final int b = 2;
    public ConcurrencyTest instance;
    @Actor
    public void actor1() {
        instance = new ConcurrencyTest();
    }
    @Actor
    public void actor2(II_Result result) {
        ConcurrencyTest c = instance;
        if (c != null) {
            result.r1 = c.a;
            result.r2 = c.b;
        } else {                   // <-- this is what you care about
            result.r1 = -1;
            result.r2 = -1;
        }
    }
}
Where do you think that the values from @Outcome(id = "0, 0") are coming from? These are the ones you set in II_Result that holds two ints, that have a default value of 0.
As such, when c == null (meaning that actor1 has not run), that if (c != null) { ...  will not be entered. So, in your code, you would do nothing : resulting in those default values of r1 and r2 being zero. You should take care of this default cases, via a simple else, like I did.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论