竞赛条件:整数的最小和最大范围

Anm*_*ggi 30 java multithreading race-condition

最近在一次采访中有人问我这个问题。

给定以下代码,静态整数的最小和最大可能值是num多少?

import java.util.ArrayList;
import java.util.List;

public class ThreadTest {
    private static int num = 0;

    public static void foo() {
        for (int i = 0; i < 5; i++) {
            num++;
        }
    }

    public static void main(String[] args) throws Exception{
        List<Thread> threads = new ArrayList<Thread>();
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Task());
            threads.add(thread);
            thread.start();
        }
        for (int i = 0; i < 5; i++) {
            threads.get(i).join();
        }
        // What will be the range of num ???
        System.out.println(ThreadTest.num);
    }
}

class Task implements Runnable {
    @Override
    public void run() {
        ThreadTest.foo();
    }

}
Run Code Online (Sandbox Code Playgroud)

我告诉他们,最大值将为25(在没有竞争条件的情况下),而最小值将为5(在每次迭代时所有线程之间的竞争条件的情况下)。
但是面试官说,最小值甚至可以低于5。这
怎么可能?

And*_*ner 33

我声称可能的最小值是2。

这样做的关键是的非原子性num++,即它是读和写,它们之间可能有其他操作。

调用线程T1..T5:

  • T1读取0,T2读取0;
  • T1写入1,然后读取和写入3次。
  • 然后T2写1;
  • 然后,T1读取为1;
  • 然后T2-5完成所有工作
  • 然后,最后,T1写入2。

(注意:如果每个线程至少有2个,结果2既不取决于线程数,也不取决于迭代数。)

对此的诚实回答是:确实没有关系。就像JLS 17.4.5中定义的那样,存在数据竞争:

当程序包含两个冲突的访问时(第17.4.1节[“如果至少一个访问是写操作,则对同一变量的两次访问(读或写)被认为是冲突的。”])通过事前发生的关系,据说它包含一个  数据竞争

(线程中的动作之间没有发生事前关系)

因此,您无法有效地依靠它的作用。这只是不正确的代码。

(而且,我知道这个问题的答案不是因为一些来之不易的战斗调试多线程代码,或深厚的技术阅读:我知道这是因为我看过之前在其他地方这个答案这是一个客厅把戏,仅此而已,所以,要求最小值不是一个很好的面试问题)。

  • @Cristik抢占T1并随后恢复运行的机制是什么,以使其不能循环5次? (2认同)