JAVA并发的三大问题-原子性问题、可见性问题和有序性问题

本文探讨了并发编程中的三个关键问题:原子性、可见性和有序性。原子性关注操作的不可中断性,例如自增操作在多线程环境下可能引发的问题。可见性问题涉及线程间数据的同步,使用volatile关键字可以解决。有序性问题由指令重排序引起,可能导致预期外的执行顺序。理解这些问题对于确保并发程序的正确性至关重要。
摘要由CSDN通过智能技术生成

(1)原子性问题:强调操作的完整性,不可被打断性。举例:将共享变量sum进行自增操作,汇编层面的操作:

  1. 获取当前sum变量的值,并且放入栈顶
  2. 将常量1放入栈顶
  3. 将当前栈顶的两个值(sum的值和1)相加,并把结果放入栈顶。
  4. 把栈顶的结果再赋值给sum变量。

ps:++操作不是原子操作,分为的4个操作之间是可以发生线程切换的,或者说是可以被其他线程中断的。

(2)可见性问题:强调一个线程的操作对另一个线程能否立刻可见。举例:

  1. 主存中有变量sum,初始值为0
  2. 线程A计划将sum加1,现将sum=0从主内存复制到自己的工作线程中,然后更新sum的值。线程A操作完成后其私有内存(工作内存)中的sum的值为1,然后线程A将更新的sum值回刷到主存的时间是不固定的。
  3. 在线程A没有回刷sum到主存前,刚好线程B同样从主存中读取sum。此时值为0,和线程A进行同样的操作,最后期盼的sum=2目标没有达成,最终sum=1

ps:线程B没有将sum变成2的原因是:线程A的修改还在其工作内存中,对线程B不可见,因为线程A的修改还没有刷入主存。这就是典型的数据不可见问题。解决方案是:使用关键字volatile修饰共享变量。

(3)有序性问题:强调程序执行的顺序和代码的先后顺序不同。举例:

        线程A和B执行的代码为:

        x=0;y=0;a=0;b=0;
        ThreadA:{
          a = 1;//①
          x = b;//②
        }
        ThreadB:{
          b = 1;//③
          y = a;//④
        }

        执行顺序可能为:①②③④:x=0,y=1;③④①②:x=1,y=0;①③②④:x=1,y=1;①③④②:x=1,y=1;③①②④:x=1,y=1;③①④②:x=1,y=1;均是正常的,但是发但并发出现的结果里面为x=0,y=0就不正常了;发生该结果的可能的执行顺序是④②①③、④②③①、②④①③、②④③①,后面的四种执行顺序有一特点:就是在线程A中存在倒序即②的执行顺序在①之前或者是线程B中存在倒序即④的顺序在③之前或者线程A、B都存在“倒序”的情况。

        ps:指令重排序是CPU为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行顺序同代码的先后顺序一致,但是会保证程序最终的执行结果和代码顺序执行的结果是一致的。反观上述例子,但对线程A来说,“倒序”并不会改变A原本的结果。同理,但对B来说,“倒序”也不会改变B原本的结果。因此有可能发生了指令重排导致的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值