Volatile关键字解读(二)

并发编程三要素

在并发编程的过程中,我们经常会遇到这三种问题:原子性、有序性和可见性

原子性

即一次操作如果涉及到一个或者多个步骤,要不全部执行成功并且中途不被打断,要么全部都不执行或者全部回滚到初态

怎么理解这段话呢?举个例子,最典型的就是转账交易,比如A向B转账100,A账户假设有100的余额,B账户假设余额是0。首先A减去100,这个时候应该给B加上100才能保证原子性,但这个时候如果B加100的操作被打断了,有个人这个时候想从B账户取100,此时就会报余额不足,这就是典型的非原子性,操作没有全部执行成功就被打断。

在比如数据存储,假设存储一个32位的数据需要分两步,首先存储底16位,然后在存储高16位。当存储底16位结束后,这个时候就去读取数据的值,显然不正确,因为高16位的值还未进行存储,这也是非原子性的案例。

可见性

主要指的是共享变量,当多个线程共同访问一个共享变量时,当其中有线程改变了变量的值时,其它线程就能立刻获取到最新的值

上一遍讲到了内存模型的实现原因,内存中的数据会向高速缓存中拷贝一个副本,而副本中数据的改变内存是不知道的,这就是不可见性,就会导致缓存不一致的问题

有序性

是指代码指令执行的顺序是按照代码的顺序依次执行的,比如

int i = 1;  //(1)
int j = 2;  //(2)
int sum = i + j; //(3)

上面的代码比较简单,两个赋值语句和一个加运算,那么上面的语句在执行的时候一定是按照(1)–(2)–(3)的顺序执行的吗?不一定,这就涉及到了另一个概念指令重排序

一般情况下,cup为了提高运行的速度和效率,会对指令按照既定的逻辑和规则进行优化,这样就会导致指令的执行可能不会按照它的顺序依次执行,但最终会保证指令执行的结果和顺序执行指令的结果是一致的。

有人会问了cup是如何做到的,这点会在下节Java内存模型中详细说明

但首次我们需要了解的一个需要遵守的原则就是有依赖关系的指令后执行:拿上面这个例子来说(3)依赖了(1)和(2),那么(3)肯定是最后一个执行,但(1)和(2)么有依赖关系,所以(1)和(2)的执行顺序就是不确定的。

在举个例子

Process processor = Context.getProcessor()   //(1)
boolean flag = true;  //(2)

while(!flag){       //(3)
   doSomething()
} 

processor.start(); //(4)

我们分析一下上面的例子,首先(1)和(2)没有依赖关系,所以顺序是不定的,(3)有依赖关系所以后执行。

假设此时有两个线程A和B来执行这个程序,因为(1)和(2)顺序不定,如果A先执行的(2),此时B线程发现(3)不满足条件,直接跳出循环去执行了(4),但是(1)并没有执行,就会报空指针异常的错误

所以有序列在单线程中是没哟影响的,但是在多线程中就会有影响,因此并发多线程要保证一致性,就必须设法保证原执行、可见性和有序列




参考信息来源:
http://www.cnblogs.com/dolphin0520/p/3920373.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值