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

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


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

这怎么可能?


阿波罗的战车
浏览 102回答 4
4回答

偶然的你

我声称可能的最小值是 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关系)所以你不能有效地依赖它所做的任何事情。这只是不正确的代码。(此外,我知道这个问题的答案并不是因为调试多线程代码的一些来之不易的战斗,或者是深入的技术阅读:我知道这一点是因为我之前在其他地方读过这个答案。这是一个客厅技巧,仅此而已,所以询问最小值不是一个很好的面试问题)。

慕森王

您的线程正在更新一个非易失性变量,这意味着它不能保证每个线程都会看到 的更新值num。让我们考虑以下线程的执行流程:Thread&nbsp;1:&nbsp;0->1->2&nbsp;(2&nbsp;iteration&nbsp;left) Thread&nbsp;2:&nbsp;0->1->2->3&nbsp;(1&nbsp;iteration&nbsp;left) Thread&nbsp;3:&nbsp;0->1->2->3&nbsp;(1&nbsp;iteration&nbsp;left) Thread&nbsp;4:&nbsp;0->1->2->3&nbsp;(1&nbsp;iteration&nbsp;left) Thread&nbsp;5:&nbsp;0->1->2->3&nbsp;(1&nbsp;iteration&nbsp;left)此时,线程 1 将 num 的值刷新到内存,线程 2,3,4,5 决定再次从内存中2读取 num (出于任何原因)。num现在:Thread&nbsp;1:&nbsp;2->3->4&nbsp;(completed&nbsp;2&nbsp;iteration) Thread&nbsp;2:&nbsp;2->3&nbsp;(completed&nbsp;1&nbsp;iteration) Thread&nbsp;3:&nbsp;2->3&nbsp;(completed&nbsp;1&nbsp;iteration) Thread&nbsp;4:&nbsp;2->3&nbsp;(completed&nbsp;1&nbsp;iteration) Thread&nbsp;5:&nbsp;2->3&nbsp;(completed&nbsp;1&nbsp;iteration)线程 1 将值刷新4到内存,之后 Theard 2,3,4.. 将值刷新到内存,显示该数字的当前值将3代替5

忽然笑

在我看来,由于缺乏原子操作,达到 25 是完全不可能的。所有线程几乎同时启动,因此每个线程都会看到与第一次迭代ThreadTest.num一样的值。0由于有 5 个线程并行访问同一变量,因此在第三次迭代时,线程可能会看到ThreadTest.num值仍然为1or 2,并且会错误地增加到2or 3。根据硬件的不同,最终值会更低或更高,最快的可能具有最低的值,最慢的可能具有较高的值。但我的主张是最大值不能达到 25。编辑 (2019-10-07)我在自己的机器(Core i5 HQ)上进行了测试,确实最终结果25几乎每次都达到了。为了更好地理解,我在循环中使用更大的数字进行了测试for:for (int i = 0; i < 10000; i++) {     num++; }现在,大多数时候,最终结果都在20000到30000之间,与50000相差甚远。

哔哔one

好吧,我的答案是 Max 25,Min 0,因为你所有的操作都是递增的,并且你将其初始化为 0.. 我认为静态非易失性 int 被扔在那里让你进入这些关于种族的想法条件,但是其中是否有任何东西可以在任何情况下减少数字?编辑:就其价值而言,这将是一种典型的干扰,他们可能希望您能够在现实世界中克服这种干扰,证明这种“欺骗”的合理性,有很多转移注意力的事情!
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java