Java:线程连接超过 10,000 次迭代不一致
Java: Thread joins over 10,000 iterations inconsistent
好的伙计们..我又回来了(最近好像是我的家)。
我正在经历在多线程上编写 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);
}
}
事情是..当我改变迭代时:
i < 10 = 20(正确)
i < 100 = 200(正确)
i < 1000 = 2000(正确)
i < 10000 = 13034 (第一个 运行)
= 14516(第二个运行)
= ...等等..
为什么它不能正确处理数以万计的迭代?
您已经演示了经典的竞争条件,当 2 个或更多线程以冲突的方式读取和写入同一个变量时,就会发生这种情况。出现这种情况是因为 ++
运算符不是原子操作——正在发生多个操作,并且线程可能会在操作之间中断,例如:
- 线程
t1
读取count
(0
),并计算增量值(1
),但它还没有将值存储回count
还没有。
- 线程
t2
读取count
(仍然是0
),并计算增量值(1
),但它还没有将值存储回count
还没有。
- 线程
t1
将其 1
值存储回 count
。
- 线程
t2
将其 1
值存储回 count
。
发生了两次更新,但最终结果只增加了 1
。不能被中断的一组操作是临界区。
这似乎发生了 20,000 - 13,034 次,或者在您的第一次处决中发生了 6,966 次。您可能对下限很幸运,但无论界限的大小如何,竞争条件都可能发生。
在Java中有几种解决方法:
- 在关键部分(
count++
行)周围放置 synchronized
blocks,锁定 this
。
- 将
count
更改为 AtomicInteger
,它自己以原子方式封装此类操作。 getAndIncrement
方法将替换此处的 ++
运算符。
好的伙计们..我又回来了(最近好像是我的家)。
我正在经历在多线程上编写 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);
}
}
事情是..当我改变迭代时:
i < 10 = 20(正确)
i < 100 = 200(正确)
i < 1000 = 2000(正确)
i < 10000 = 13034 (第一个 运行)
= 14516(第二个运行)
= ...等等..
为什么它不能正确处理数以万计的迭代?
您已经演示了经典的竞争条件,当 2 个或更多线程以冲突的方式读取和写入同一个变量时,就会发生这种情况。出现这种情况是因为 ++
运算符不是原子操作——正在发生多个操作,并且线程可能会在操作之间中断,例如:
- 线程
t1
读取count
(0
),并计算增量值(1
),但它还没有将值存储回count
还没有。 - 线程
t2
读取count
(仍然是0
),并计算增量值(1
),但它还没有将值存储回count
还没有。 - 线程
t1
将其1
值存储回count
。 - 线程
t2
将其1
值存储回count
。
发生了两次更新,但最终结果只增加了 1
。不能被中断的一组操作是临界区。
这似乎发生了 20,000 - 13,034 次,或者在您的第一次处决中发生了 6,966 次。您可能对下限很幸运,但无论界限的大小如何,竞争条件都可能发生。
在Java中有几种解决方法:
- 在关键部分(
count++
行)周围放置synchronized
blocks,锁定this
。 - 将
count
更改为AtomicInteger
,它自己以原子方式封装此类操作。getAndIncrement
方法将替换此处的++
运算符。