插入 System.out.println 调用会影响竞争条件:如何调试?

Inserting System.out.println calls affects race conditions: how to debug?

我不明白为什么添加println注释后下面代码中的竞争条件消失了。 这确实使调试变得困难。

那我们如何调试这些条件呢?

在下面的代码中,机器应该抛出异常,因为两个线程以不一致的方式修改了 class 的内容。

事实上检查代码 b 应该总是大于或等于 a(class 合同)。然而,由于两个线程之间的竞争条件,这个契约不满足。

没有注释,竞争条件发生,代码抛出异常,但是有 System.out.println 注释,我没有得到任何竞争条件。

Java 版本 1.8.0_112

package lectures;

import java.util.Random;

public class Concurrency1 {
    int a,b;
    void setEqual(int x){
//      System.out.println("setEqual "+x);
        a=x;
        b=x;
    }

    void setRel(){
//      System.out.println("setRel");
        a++;
        b+=a;
    }

    void checkContract() throws Exception{
        if (b<a) throw new Exception("b<a!!, a="+a+" b="+b); 
    }

    public static void main(String[] args) throws Exception{
        Concurrency1 c1=new Concurrency1();
        final  int count=10000000;
        Thread t1=new Thread(){
            public void run(){
                Random gen=new Random();
                int c=0;
                while(c++<count){
                    System.out.println("setEqual ");
                    c1.setEqual(gen.nextInt(10));
                }
        }};
        Thread t2=new Thread(){
            public void run(){
                int c=0;
                while(c++<count){
                    System.out.println("setGen ");
                    c1.setRel();
                }
        }};
        t1.start();
        t2.start();
        int c=0;
        while(t1.isAlive()||t2.isAlive()||c++<count){
            c1.checkContract(); 
        }
    }
}

如果没有 println 消息,我会收到如下异常消息:

Exception in thread "main" java.lang.Exception: b<a!!, a=104 b=27966
    at lectures.Concurrency1.checkContract(Concurrency1.java:20)
    at lectures.Concurrency1.main(Concurrency1.java:48)

检查完成后更改值的位置... 这与 Loop doesn't see changed value without a print statement 不同,因为线程正在看到彼此的更改并因此生成异常

System.out::println 包含一个 synchronized 块,它导致 Threads 逐个顺序访问资源。

来自PrintStream.java

public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}

可能充当过滤器。对方法 setRelsetEqual 的多线程调用首先由 System.out::println 组织,然后仅 一次 .[=18 执行剩余的语句=]

虽然不确定。