为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

潇湘沐 2019-08-19 10:38:39
为什么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();     }}
查看完整描述

3 回答

?
缥缈止盈

TA贡献2041条经验 获得超4个赞

i++在Java中可能不是原子的,因为原子性是一个特殊的要求,在大多数用途中都不存在i++。该要求具有显着的开销:使增量操作成为原子的成本很高; 它涉及在普通增量中不需要存在的软件和硬件级别的同步。

您可以将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); 因为使用原子是不合适的i++。更糟糕的是,程序员从C语言或其他类C语言到Java都会使用i++,导致不必要地使用原子指令。

即使在机器指令集级别,由于性能原因,增量类型操作通常也不是原子的。在x86中,必须使用特殊指令“lock prefix”来使inc指令成为原子:出于与上述相同的原因。如果inc总是原子的,那么当需要非原子公司时,它永远不会被使用; 程序员和编译器会生成加载,添加1和存储的代码,因为它会更快。

在一些指令集架构中,没有原子inc或根本没有inc; 要在MIPS上执行原子公司,你必须编写一个使用lland sc:load-linked和store-conditional 的软件循环。加载链接读取单词,如果单词未更改,则store-conditional存储新值,否则失败(检测到并导致重新尝试)。


查看完整回答
反对 回复 2019-08-19
?
四季花海

TA贡献1811条经验 获得超5个赞

i++ 涉及两个操作:


读取当前值 i

递增值并将其分配给 i

当两个线程i++同时对同一个变量执行时,它们可能都获得相同的当前值i,然后递增并设置为i+1,因此您将获得单个增量而不是两个。


示例:


int i = 5;

Thread 1 : i++;

           // reads value 5

Thread 2 : i++;

           // reads value 5

Thread 1 : // increments i to 6

Thread 2 : // increments i to 6

           // i == 6 instead of 7


查看完整回答
反对 回复 2019-08-19
?
守着星空守着你

TA贡献1799条经验 获得超8个赞

为什么i ++在Java中不是原子的?

让我们将增量操作分解为多个语句:

线程1和2:

  1. 从内存中获取总值

  2. 将1添加到值

  3. 写回内存

如果没有同步,那么让我们说线程1读取值3并将其增加到4,但是没有写回来。此时,发生上下文切换。线程2读取值3,递增它并发生上下文切换。虽然两个线程都增加了总值,但仍然是4 - 竞争条件


查看完整回答
反对 回复 2019-08-19
  • 3 回答
  • 0 关注
  • 689 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号