简体   繁体   English

比赛条件:integer 的最小和最大范围

[英]Race condition: Min and Max range of an integer

I was recently asked this question in an interview.我最近在一次采访中被问到这个问题。

Given the following code, what will be the min and max possible value of the static integer num ?给定以下代码, static integer 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();
    }

}

I told them that the max value would be 25 (in case there is no race condition), and min would be 5 (in case of a race condition between all the threads at every iteration).我告诉他们最大值是 25(如果没有竞争条件),最小值是 5(如果每次迭代中所有线程之间都存在竞争条件)。
But the interviewer said the the min value can go even below 5.但是面试官说最小值可以 go 甚至低于 5。
How is that possible?这怎么可能?

I claim the minimum value possible is 2.我声称可能的最小值是 2。

The key to this is the non-atomicity of num++ , ie, it is a read and a write, which may have other operations in between.关键是num++的非原子性,即它是一个读和一个写,其间可能有其他操作。

Call the threads T1..T5:调用线程 T1..T5:

  • T1 reads 0, T2 reads 0; T1 读 0,T2 读 0;
  • T1 writes 1, and then reads and writes 3 times. T1写入1,然后读写3次。
  • Then T2 writes 1;然后T2写1;
  • Then T1 reads 1;然后T1读1;
  • Then T2-5 do all of their work然后 T2-5 完成所有工作
  • Then, finally, T1 writes 2.然后,最后,T1 写入 2。

(Note: the result 2 is not dependent either on the number of threads, or the number of iterations, provided there are at least 2 of each.) (注意:结果 2 不依赖于线程数或迭代次数,前提是每个线程至少有 2 个。)

But the honest answer to this is: it really doesn't matter.但对此的诚实答案是:这真的不重要。 There is a data race, as defined in JLS 17.4.5 :JLS 17.4.5中所定义,存在数据竞争:

When a program contains two conflicting accesses (§17.4.1 ["Two accesses to (reads of or writes to) the same variable are said to be conflicting if at least one of the accesses is a write."]) that are not ordered by a happens-before relationship, it is said to contain a data race .当程序包含两个冲突的访问(第 17.4.1 节 [“如果至少有一个访问是写入,则对同一变量的两次访问(读取或写入)称为冲突。”])没有排序通过happens-before关系,它被称为包含数据竞争

(There is an absence of happens-before relationships between the actions in the threads) (线程中的动作之间没有发生之前的关系)

So you can't usefully rely on whatever it does.所以你不能有用地依赖它所做的任何事情。 It is simply incorrect code.这只是不正确的代码。

(Moreover, I know the answer to this not because of some hard-won battle debugging multithreaded code, or deep technical reading: I know this because I have read this answer before elsewhere. It's a parlour trick, nothing more, and so asking the minimum value isn't a very good interview question). (此外,我知道这个问题的答案并不是因为一些来之不易的多线程代码调试或深入的技术阅读:我知道这一点是因为我以前在其他地方读过这个答案。这是一个客厅把戏,仅此而已,所以问最小值不是一个很好的面试问题)。

Your threads are updating a variable which is is not volatile that means it does not guarantee that every thread will see the updated value of num .您的线程正在更新一个非易失性变量,这意味着它不能保证每个线程都会看到num的更新值。 Let consider the below execution flow of threads:让我们考虑以下线程的执行流程:

Thread 1: 0->1->2 (2 iteration left)
Thread 2: 0->1->2->3 (1 iteration left)
Thread 3: 0->1->2->3 (1 iteration left)
Thread 4: 0->1->2->3 (1 iteration left)
Thread 5: 0->1->2->3 (1 iteration left)

At this point, Thread 1 flushes the value 2 of num to memory and Thread 2,3,4,5 decide to read the num from the memory again (for any reason).此时,线程 1 将 num 的值2刷新到 memory 并且线程 2、3、4、5 决定再次从 memory 读取num (出于任何原因)。 Now:现在:

Thread 1: 2->3->4 (completed 2 iteration)
Thread 2: 2->3 (completed 1 iteration)
Thread 3: 2->3 (completed 1 iteration)
Thread 4: 2->3 (completed 1 iteration)
Thread 5: 2->3 (completed 1 iteration)

Thread 1 flushes the value 4 to the memory and after that Theard 2,3,4.. flushes the value to the memory show the current value of the number will be 3 instead of 5线程 1 将值4刷新到 memory 之后,Theard 2,3,4.. 将值刷新到 memory 显示数字的当前值将是3而不是5

In my opinion, it's quite impossible to reach 25 due to the lack of atomic operations (see Atomic Access in Java Tutorials).在我看来,由于缺乏原子操作,达到 25 是完全不可能的(参见 Java 教程中的 原子访问)。

All threads start at almost the same time, so every thread sees ThreadTest.num value as 0 in the first iteration.所有线程几乎在同一时间启动,因此每个线程在第一次迭代中都将ThreadTest.num值视为0 Since there are 5 threads accessing the same variable in parallel, at the third iteration the threads are likely to see ThreadTest.num value still as 1 or 2 and are going to increment wrongly to 2 or 3 .由于有 5 个线程并行访问同一个变量,因此在第三次迭代中,线程可能会看到ThreadTest.num值仍然为12并且会错误地增加到23

Depending on hardware the final value is going to be lower or higher, the fastest ones might have the lowest values and the slowest ones the higher values.根据硬件,最终值会更低或更高,最快的可能有最低的值,最慢的可能有更高的值。 But still, my claim is the maximum value cannot reach 25.但是,我的主张是最大值不能达到 25。

EDIT (2019-10-07)编辑(2019-10-07)

I tested myself in my machine (Core i5 HQ), indeed the final result reached 25 almost all the times.我在我的机器(Core i5 HQ)上测试了自己,确实最终结果几乎每次都达到了25 To understand better, I tested with a bigger number in for loop:为了更好地理解,我在for循环中测试了一个更大的数字:

for (int i = 0; i < 10000; i++) {
    num++;
}

Now, most of times, the final result was between 20000 and 30000, well far from 50000.现在,大多数时候,最终结果在 20000 到 30000 之间,与 50000 相差甚远。

Well, My answer would be Max 25, and Min 0, since all of your operations are increment, and you initialized it as 0.. I think the static non-volatile int is thrown in there to make you go into these thoughts about race conditions, but is there anything in there that would DECREMENT the number in any situation?好吧,我的答案是 Max 25,Min 0,因为你所有的操作都是递增的,并且你将它初始化为 0.. 我认为 static 非易失性 int 被扔在那里让你 go 进入这些关于种族的想法条件,但是在任何情况下有什么东西会减少这个数字吗?

Edit: For what it's worth, this would be a typical distraction that they may expect you to be able to overcome in the real world, justifying such "trickery", there are a lot of red herrings!编辑:对于它的价值,这将是他们可能希望你能够在现实世界中克服的典型分心,证明这种“诡计”是正当的,有很多红鲱鱼!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM