使用Java ThreadSanitizer发现data race

这个功能我没有明白为什么没有加入默认的Java版本中,这个东西应该是很有用的东西。可能是效果不是特别好,也可能是存在某些明显的性能开销,以至于在代码中维护这些东西成本非常好而收益很低。

UPDATE: 效率是真的低,我感觉可能是10x-20x差不多左右。

https://runtimeverification.com/blog/detecting-popular-data-races-in-java-using-rv-predict/


openjdk/tsan: https://openjdk.org/projects/tsan

编译命令是

use-java13; unset CLASSPATH; bash configure –prefix=`pwd`/installed ; make CONF=linux-x86_64-server-release clean install

简单的Java程序,多个线程共同修改某个变量

package com.starrocks.lab;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadDataRaceCase {
    int value;

    public class Adder implements Runnable {
        int index;
        int duration;
        int tick;

        public Adder(int index, int duration) {
            this.index = index;
            this.duration = duration;
            this.tick = 0;
        }

        @Override
        public void run() {
            System.out.println("Thread " + index + " started...");
            long begin = System.currentTimeMillis();
            while (true) {
                value += 1;
                tick += 1;
                if (tick % 100 == 0) {
                    long now = System.currentTimeMillis();
                    if ((now - begin) > duration * 1000) {
                        break;
                    }
                }
            }
            System.out.println("Thread " + index + " stopped...");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadDataRaceCase t = new ThreadDataRaceCase();
        t.value = 0;

        int number = 16;
        int duration = 10;
        Adder adder[] = new Adder[number];
        ExecutorService executor = Executors.newFixedThreadPool(number);
        for (int i = 0; i < number; i++) {
            adder[i] = t.new Adder(i, duration);
            executor.submit(adder[i]);
        }

        executor.shutdown();
        executor.awaitTermination(2 * duration, TimeUnit.SECONDS);
    }
}

运行起来可以发现错误是:需要使用 `java -XX:+ThreadSanitizer` 来运行

sandbox-cloud :: src/main/java ‹master*› » javac com/starrocks/lab/ThreadDataRaceCase.java
sandbox-cloud :: src/main/java ‹master*› » java -XX:+ThreadSanitizer com/starrocks/lab/ThreadDataRaceCase
Thread 1 started...
Thread 3 started...
==================
WARNING: ThreadSanitizer: data race (pid=227159)
  Read of size 4 at 0x000101c92a64 by thread T18:
    #0 com.starrocks.lab.ThreadDataRaceCase$Adder.run()V ThreadDataRaceCase.java:26
    #1 java.util.concurrent.Executors$RunnableAdapter.call()Ljava/lang/Object; Executors.java:515
    #2 java.util.concurrent.FutureTask.run()V FutureTask.java:264
    #3 java.util.concurrent.ThreadPoolExecutor.runWorker(Ljava/util/concurrent/ThreadPoolExecutor$Worker;)V ThreadPoolExecutor.java:1130
    #4 java.util.concurrent.ThreadPoolExecutor$Worker.run()V ThreadPoolExecutor.java:630
    #5 java.lang.Thread.run()V Thread.java:832
    #6 (Generated Stub) <null>

  Previous write of size 4 at 0x000101c92a64 by thread T16:
    #0 com.starrocks.lab.ThreadDataRaceCase$Adder.run()V ThreadDataRaceCase.java:26
    #1 java.util.concurrent.Executors$RunnableAdapter.call()Ljava/lang/Object; Executors.java:515
    #2 java.util.concurrent.FutureTask.run()V FutureTask.java:264
    #3 java.util.concurrent.ThreadPoolExecutor.runWorker(Ljava/util/concurrent/ThreadPoolExecutor$Worker;)V ThreadPoolExecutor.java:1130
    #4 java.util.concurrent.ThreadPoolExecutor$Worker.run()V ThreadPoolExecutor.java:630
    #5 java.lang.Thread.run()V Thread.java:832
    #6 (Generated Stub) <null>

  Thread T18 (tid=227178, running) created by thread T1 at:
    #0 pthread_create <null> (libtsan.so.0+0x615d8)
    #1 os::create_thread(Thread*, os::ThreadType, unsigned long) /home/disk4/zhangyan/repo/tsan/src/hotspot/os/linux/os_linux.cpp:927 (libjvm.so+0xb4e649)
    #2 java.lang.Thread.start()V Thread.java:801
    #3 java.util.concurrent.ThreadPoolExecutor.addWorker(Ljava/lang/Runnable;Z)Z ThreadPoolExecutor.java:939
    #4 java.util.concurrent.ThreadPoolExecutor.execute(Ljava/lang/Runnable;)V ThreadPoolExecutor.java:1345
    #5 java.util.concurrent.AbstractExecutorService.submit(Ljava/lang/Runnable;)Ljava/util/concurrent/Future; AbstractExecutorService.java:118
    #6 com.starrocks.lab.ThreadDataRaceCase.main([Ljava/lang/String;)V ThreadDataRaceCase.java:49
    #7 (Generated Stub) <null>

  Thread T16 (tid=227176, running) created by thread T1 at:
    #0 pthread_create <null> (libtsan.so.0+0x615d8)
    #1 os::create_thread(Thread*, os::ThreadType, unsigned long) /home/disk4/zhangyan/repo/tsan/src/hotspot/os/linux/os_linux.cpp:927 (libjvm.so+0xb4e649)
    #2 java.lang.Thread.start()V Thread.java:801
    #3 java.util.concurrent.ThreadPoolExecutor.addWorker(Ljava/lang/Runnable;Z)Z ThreadPoolExecutor.java:939
    #4 java.util.concurrent.ThreadPoolExecutor.execute(Ljava/lang/Runnable;)V ThreadPoolExecutor.java:1345
    #5 java.util.concurrent.AbstractExecutorService.submit(Ljava/lang/Runnable;)Ljava/util/concurrent/Future; AbstractExecutorService.java:118
    #6 com.starrocks.lab.ThreadDataRaceCase.main([Ljava/lang/String;)V ThreadDataRaceCase.java:49
    #7 (Generated Stub) <null>