好的伙计们......我又回来了(最近似乎是我的家)。
我正在浏览关于多线程的YouTube视频编程的整个洞穴。这个特定的一个使用2个线程,通过一个for循环,每个变量加1到一个变量10,000。所以你加入他们,结果就是20,000。
public class main {
private int count = 0;
public static void main(String[] args) {
main main = new main();
main.doWork();
}
public void doWork(){
Thread t1 = new Thread(new Runnable(){
public void run(){
for (int i = 0; i < 10000; i++){
count++;
}
}
});
Thread t2 = new Thread(new Runnable(){
public void run(){
for (int i = 0; i < 10000; i++){
count++;
}
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException ex) {
Logger.getLogger(main.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("Count is: " + count);
}
}
当我改变迭代时,事情就是......
我&lt; 10 = 20(正确)
我&lt; 100 = 200(正确)
我&lt; 1000 = 2000(正确)
我&lt; 10000 = 13034(首次运行)
= 14516(第二次运行)
= ......等..
为什么它不能正确处理成千上万的迭代?
答案 0 :(得分:1)
您已经演示了经典的竞争条件,当两个或多个线程以相互冲突的方式读取和写入同一个变量时会发生这种情况。这是因为++
运算符不是原子操作 - 正在发生多个操作,并且在操作之间可能会中断线程,例如:
t1
读取count
(0
),并计算递增的值(1
),但尚未将值存储回count
尚未。t2
读取count
(仍为0
),并计算递增的值(1
),但尚未将值存储回{{1但是。count
将其t1
值存储回1
。count
将其t2
值存储回1
。发生了两次更新,但最终结果只增加了count
。必须不中断的操作集是关键部分。
这似乎发生了20,000到13,034次,或者在你第一次执行时发生了6,966次。你可能已经幸运得到了下限,但不管界限的大小,都可能发生竞争条件。
在Java中,有几种解决方案:
synchronized
blocks放在关键部分(1
行)周围,锁定count++
。this
更改为AtomicInteger
,它会自行封装此类操作。 count
方法将替换此处的getAndIncrement
运算符。