为什么i ++不是原子的?

为什么i ++不是原子的?

为什么i++Java中没有原子?

为了更深入地了解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的线+线程2的线与打印出的总线数不匹配。

这是我的代码:

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();
    }}


潇湘沐
浏览 690回答 3
3回答

缥缈止盈

i++在Java中可能不是原子的,因为原子性是一个特殊的要求,在大多数用途中都不存在i++。该要求具有显着的开销:使增量操作成为原子的成本很高;&nbsp;它涉及在普通增量中不需要存在的软件和硬件级别的同步。您可以将i++应该设计和记录的参数设置为专门执行原子增量,以便使用执行非原子增量i = i + 1。但是,这会破坏Java,C和C ++之间的“文化兼容性”。同样,它会删除一个方便的符号,熟悉类C语言的程序员认为这是理所当然的,赋予它一个特殊的含义,仅适用于有限的情况。基本的C或C ++代码就像for (i = 0; i < LIMIT; i++)转换为Java一样for (i = 0; i < LIMIT; i = i + 1);&nbsp;因为使用原子是不合适的i++。更糟糕的是,程序员从C语言或其他类C语言到Java都会使用i++,导致不必要地使用原子指令。即使在机器指令集级别,由于性能原因,增量类型操作通常也不是原子的。在x86中,必须使用特殊指令“lock prefix”来使inc指令成为原子:出于与上述相同的原因。如果inc总是原子的,那么当需要非原子公司时,它永远不会被使用;&nbsp;程序员和编译器会生成加载,添加1和存储的代码,因为它会更快。在一些指令集架构中,没有原子inc或根本没有inc;&nbsp;要在MIPS上执行原子公司,你必须编写一个使用lland&nbsp;sc:load-linked和store-conditional&nbsp;的软件循环。加载链接读取单词,如果单词未更改,则store-conditional存储新值,否则失败(检测到并导致重新尝试)。

四季花海

i++ 涉及两个操作:读取当前值 i递增值并将其分配给 i当两个线程i++同时对同一个变量执行时,它们可能都获得相同的当前值i,然后递增并设置为i+1,因此您将获得单个增量而不是两个。示例:int i = 5;Thread 1 : i++;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// reads value 5Thread 2 : i++;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// reads value 5Thread 1 : // increments i to 6Thread 2 : // increments i to 6&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// i == 6 instead of 7

守着星空守着你

为什么i ++在Java中不是原子的?让我们将增量操作分解为多个语句:线程1和2:从内存中获取总值将1添加到值写回内存如果没有同步,那么让我们说线程1读取值3并将其增加到4,但是没有写回来。此时,发生上下文切换。线程2读取值3,递增它并发生上下文切换。虽然两个线程都增加了总值,但仍然是4 - 竞争条件
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java