为什么i ++不是原子的? [英] Why is i++ not atomic?
问题描述
为什么 i ++
在Java中不是原子的?
所以我使用了一个
private static int total = 0;
。
有两个线程。
- 线程1:打印
System.out.println(Hello from Thread 1! ;
- 线程2:打印
System.out.println(Hello from Thread 2!);
我计算线程1和线程2打印的线。但是线程1 +
<$>这是我的代码: c $ c> 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){
总++;
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();
}
}
code> i ++ 在Java中可能不是原子的,因为atomicity是一个特殊的要求,在 i ++
。这个需求有一个重要的开销:使增量操作原子化的成本很高;
你可以使用 i ++
i = i + 1
执行非原子增量。但是,这将打破Java,C和C ++之间的文化兼容性。同样,它会带走熟悉C语言的程序员认为理所当然的方便符号,给它一个特殊的意义,只适用于有限的情况。 基本类似 for(i = 0; i
; LIMIT; i = i + 1)
;因为使用atomic i ++
是不合适的。更糟糕的是,程序员从C或其他类C语言来到Java会使用 i ++
,导致不必要的使用原子指令。
即使在机器指令集级别,由于性能原因,增量类型操作通常不是原子操作。在x86中,必须使用特殊指令锁定前缀来使 inc
指令原子:与上述相同的原因。如果 inc
始终是原子的,当需要非原子inc时永远不会使用它;程序员和编译器将生成加载,添加1和存储的代码,因为它会更快。
在某些指令集架构中,没有原子 inc
或者可能没有 inc
在MIPS上做一个原子公司,你必须编写一个使用 ll
和 sc
:load-链接和存储条件。加载链接读取字,存储条件存储新值,如果单词没有改变,否则它失败(检测到并导致重试)。
Why is i++
not atomic in Java?
To get a bit deeper in Java I tried to count how often the loop in threads are executed.
So I used a
private static int total = 0;
in the main class.
I have two threads.
- Thread 1: Prints
System.out.println("Hello from Thread 1!");
- Thread 2: Prints
System.out.println("Hello from Thread 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.
Here is my code:
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++
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.
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.
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.
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.
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屋!