首页 > 解决方案 > 竞争条件:整数的最小值和最大值范围

问题描述

我最近在一次采访中被问到这个问题。

给定以下代码,静态整数的最小和最大可能值是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();
    }

}

我告诉他们最大值是 25(如果没有竞争条件),最小值是 5(如果每次迭代中所有线程之间都存在竞争条件)。
但是面试官说最小值甚至可以低于5。
这怎么可能?

标签: javamultithreadingrace-condition

解决方案


我声称可能的最小值是 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 节 [“如果至少有一个访问是写入,则对同一变量的两次访问(读取或写入)称为冲突。”])没有排序通过happens-before关系,它被称为包含 数据竞争

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

所以你不能有用地依赖它所做的任何事情。这只是不正确的代码。

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


推荐阅读