为什么i++不是原子性操作

为啥i++并不是原子性操作

在编写多线程程序时,对于不是原子操作的,需要引起程序员的额外注意。
一定要确保其对数据的操作是同步的,否则会引发数据安全问题。

i++不是原子操作

先来看一个例子,多线程下出现的数据不一致问题。

public class Test {
	static int i = 0;
	
	public static void main(String[] args) {
		for (int j = 0; j < 100; j++) {
			new Thread(() -> {
				//休眠5毫秒,结果更明显
				SleepUtil.MILL.sleep(5);
				i++;
			}).start();
		}
		SleepUtil.SEC.sleep(1);
		System.out.println(i);
	}
}
123456789101112131415

输出如下:

93 //输出的值不确定
1

启动100个线程去对i++,最终结果却是小于100。

为什么不是原子操作

再来看一个例子。

public class Test {
	static int i = 0;
	public static void main(String[] args) {
		i++;
	}
}
123456

将Test.java编译成class文件,然后利用javap -c 进行反汇编。
获得如下文件:

Compiled from "Test.java"
public class item02.tool.Test {
  static int i;

  public item02.tool.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       //获取指定类的静态域,并将其值压入栈顶
       0: getstatic     #2                  // Field i:I
       //将int型1推送至栈顶
       3: iconst_1
       //将栈顶两int型数值相加并将结果压入栈顶
       4: iadd
       //为指定的类的静态域赋值
       5: putstatic     #2                  // Field i:I
       8: return

  static {};
    Code:
       0: iconst_0
       1: putstatic     #2                  // Field i:I
       4: return
}
12345678910111213141516171819202122232425262728

Java代码最终会被编译成一条条指令,线程在执行每一条指令前都随时有可能会失去CPU的执行权。

对于i++操作,可以看到最终被编译成如下四条指令:

//获取指定类的静态域,并将其值压入栈顶
0: getstatic     #2                  // Field i:I
//将int型1推送至栈顶
3: iconst_1
//将栈顶两int型数值相加并将结果压入栈顶
4: iadd
//为指定的类的静态域赋值
5: putstatic     #2                  // Field i:I
12345678

先取值,然后加1,最后赋值给静态变量。

线程很可能取完值,就失去了CPU的执行权,然后其他线程取得了相同的i值,导致即使多个线程+1,其实最终结果也只加了1而已,最终输出i就小于100了。

使用字节码来分析并发问题并不严谨,因为即使编译出来只有一条JVM指令,也并不意味着这条指令的操作是原子的。一条JVM指令在解释执行时,解释器需要执行很多行代码才能完成它的语义。如果是编译执行,一条JVM指令也可能被编译成多行本地机器码指令。如果需要更加严谨的分析,可以通过反汇编

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT二叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值