为什么 i++ 不是原子的? [英] Why is i++ not atomic?
问题描述
为什么 i++
在 Java 中不是原子的?
Why is i++
not atomic in Java?
为了更深入地了解 Java,我尝试计算线程中循环的执行频率.
To get a bit deeper in Java I tried to count how often the loop in threads are executed.
所以我用了一个
private static int total = 0;
在主类中.
我有两个线程.
- 线程 1:打印
System.out.println("Hello from Thread 1!");
- 线程 2:打印
System.out.println("Hello from Thread 2!");
然后我计算了线程 1 和线程 2 打印的行数.但是线程 1 的行数 + 线程 2 的行数与打印出来的总行数不匹配.
And I count the lines printed by thread 1 and thread 2. But the lines of thread 1 + lines of thread 2 don't match the total number of lines printed out.
这是我的代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Test {
private static int total = 0;
private static int countT1 = 0;
private static int countT2 = 0;
private boolean run = true;
public Test() {
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
newCachedThreadPool.execute(t1);
newCachedThreadPool.execute(t2);
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
run = false;
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println((countT1 + countT2 + " == " + total));
}
private Runnable t1 = new Runnable() {
@Override
public void run() {
while (run) {
total++;
countT1++;
System.out.println("Hello #" + countT1 + " from Thread 2! Total hello: " + total);
}
}
};
private Runnable t2 = new Runnable() {
@Override
public void run() {
while (run) {
total++;
countT2++;
System.out.println("Hello #" + countT2 + " from Thread 2! Total hello: " + total);
}
}
};
public static void main(String[] args) {
new Test();
}
}
推荐答案
i++
在 Java 中可能不是原子性的,因为原子性是一种特殊要求,在 i++
.这个要求有很大的开销:使增量操作原子化需要很大的成本;它涉及不需要以普通增量出现的软件和硬件级别的同步.
i++
is probably not atomic in Java because atomicity is a special requirement which is not present in the majority of the uses of i++
. That requirement has a significant overhead: there is a large cost in making an increment operation atomic; it involves synchronization at both the software and hardware levels that need not be present in an ordinary increment.
您可以论证 i++
应该被设计和记录为专门执行原子增量,以便使用 i = i + 1来执行非原子增量代码>.但是,这会破坏 Java 与 C 和 C++ 之间的文化兼容性".此外,它还将取消熟悉类 C 语言的程序员认为理所当然的便利符号,赋予它仅适用于有限情况的特殊含义.
You could make the argument that i++
should have been designed and documented as specifically performing an atomic increment, so that a non-atomic increment is performed using i = i + 1
. However, this would break the "cultural compatibility" between Java, and C and C++. As well, it would take away a convenient notation which programmers familiar with C-like languages take for granted, giving it a special meaning that applies only in limited circumstances.
像 for (i = 0; i < LIMIT; i++)
这样的基本 C 或 C++ 代码将转换为 Java 为 for (i = 0; i
i++
是不合适的.更糟糕的是,从 C 或其他类似 C 语言到 Java 的程序员无论如何都会使用 i++
,从而导致不必要地使用原子指令.
Basic C or C++ code like for (i = 0; i < LIMIT; i++)
would translate into Java as for (i = 0; i < LIMIT; i = i + 1)
; because it would be inappropriate to use the atomic i++
. What's worse, programmers coming from C or other C-like languages to Java would use i++
anyway, resulting in unnecessary use of atomic instructions.
即使在机器指令集级别,出于性能原因,增量类型的操作通常也不是原子的.在 x86 中,必须使用特殊指令锁定前缀"来使 inc
指令原子化:出于与上述相同的原因.如果 inc
总是原子的,那么当需要非原子的 inc 时它永远不会被使用;程序员和编译器会生成加载、加 1 和存储的代码,因为这样会更快.
Even at the machine instruction set level, an increment type operation is usually not atomic for performance reasons. In x86, a special instruction "lock prefix" must be used to make the inc
instruction atomic: for the same reasons as above. If inc
were always atomic, it would never be used when a non-atomic inc is required; programmers and compilers would generate code that loads, adds 1 and stores, because it would be way faster.
在一些指令集架构中,没有原子的inc
,或者根本没有inc
;要在 MIPS 上执行原子公司,您必须编写一个使用 ll
和 sc
的软件循环:加载链接和存储条件.Load-linked 读取单词,如果单词没有改变,store-conditional 存储新值,否则失败(被检测到并导致重试).
In some instruction set architectures, there is no atomic inc
or perhaps no inc
at all; to do an atomic inc on MIPS, you have to write a software loop which uses the ll
and sc
: load-linked, and store-conditional. Load-linked reads the word, and store-conditional stores the new value if the word has not changed, or else it fails (which is detected and causes a re-try).
这篇关于为什么 i++ 不是原子的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!