我最近在一次采访中被问到这个问题。
给定以下代码,静态整数的最小和最大可能值是多少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。
这怎么可能?
我声称可能的最小值是 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 https://docs.oracle.com/javase/specs/jls/se12/html/jls-17.html#jls-17.4.5:
当程序包含两个未排序的冲突访问时(第 17.4.1 节 [“如果至少其中一个访问是写入,则对同一变量的两次访问(读取或写入)被认为是冲突的。”])通过发生在关系之前,据说它包含一个数据竞赛.
(缺少发生在之前线程中动作之间的关系)
所以你不能有效地依赖它所做的任何事情。这只是不正确的代码。
(此外,我知道这个问题的答案不是因为调试多线程代码的一些来之不易的战斗,或者是深入的技术阅读:我知道这一点是因为我之前在其他地方读过这个答案。这是一个客厅技巧,仅此而已,所以询问最小值这不是一个很好的面试问题)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)