i++ 或 ++i 是原子操作吗?

前面写了一篇文章,讨论了i++和++i的区别(https://blog.csdn.net/lijianqingfeng/article/details/103474225)。该文的前提都是不存在并发的情况,通过i++ 和++i编译后的代码分析其执行效果。

public class Test {
    private static volatile int count = 0;

    public static void increment() {
        count++;
    }

    public static void main(String[] args) throws InterruptedException {
        IntStream.range(0, 10)
                .forEach(i ->
                        new Thread(() -> IntStream.range(0, 1000).forEach(j -> increment())).start());
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(count);
    }
}

程序运行结果并不是10000, 而是一个不确定的数,说明在i++并不是原子操作(即便是已经加上了volatile)。

为什么呢?看一下编译后的increment方法代码。

 public static void increment();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #2                  // Field count:I
         3: iconst_1
         4: iadd
         5: putstatic     #2                  // Field count:I
         8: return
      LineNumberTable:
        line 11: 0
        line 12: 8

突然发现,这里不是用的iinc了。count是static的,在内存中应该是放在方法区内。所以每次使用和保存都需要用getstatic和putstatic。

 

那么,修改成类变量呢?

public class Test {
    private volatile int count = 0;

    public void increment() {
        count++;
    }

    public static void main(String[] args) {
        Test test = new Test();
        IntStream.range(0, 10)
                .forEach(i ->
                        new Thread(() -> IntStream.range(0, 1000).forEach(j -> test.increment())).start());
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(test.count);
    }
}
  public void increment();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: dup
         2: getfield      #2                  // Field count:I
         5: iconst_1
         6: iadd
         7: putfield      #2                  // Field count:I
        10: return
      LineNumberTable:
        line 11: 0
        line 12: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   Lcom/baidu/adu/test/Test;

发现,这里也不是iinc,用的是getfield,从编译结果的命令上已经不是原子性的了,所以整个++操作肯定不是原子的。

至于iinc是否原子操作也无从分析了。。。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值