为什么 i++ 不是原子的? [英] Why is i++ not atomic?

查看:26
本文介绍了为什么 i++ 不是原子的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么 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 上执行原子公司,您必须编写一个使用 llsc 的软件循环:加载链接和存储条件.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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆