Java并发编程——volatile

本文解析了volatile关键字在多线程环境中的可见性与原子性问题,通过实例说明了自增操作的非原子性可能导致的并发结果不一致。重点强调了volatile如何确保可见性,但不能保证操作的原子性,以及这个误解在代码执行中的表现。
摘要由CSDN通过智能技术生成

原文:https://www.cnblogs.com/dolphin0520/p/3920373.html

其中有一段说

volatile 不具有原子性。并且给了一个例子:

//代码

大家想一下这段程序的输出结果是多少?也许有些朋友认为是10000。但是事实上运行它会发现每次运行结果都不一致,都是一个小于10000的数字。
  可能有的朋友就会有疑问,不对啊,上面是对变量inc进行自增操作,由于volatile保证了可见性,那么在每个线程中对inc自增完之后,在其他线程中都能看到修改后的值啊,所以有10个线程分别进行了1000次操作,那么最终inc的值应该是1000*10=10000。
  这里面就有一个误区了,volatile关键字能保证可见性没有错,但是上面的程序错在没能保证原子性。可见性只能保证每次读取的是最新的值,但是volatile没办法保证对变量的操作的原子性。
  在前面已经提到过,自增操作是不具备原子性的,它包括读取变量的原始值、进行加1操作、写入工作内存。那么就是说自增操作的三个子操作可能会分割开执行,就有可能导致下面这种情况出现:
  假如某个时刻变量inc的值为10,
  线程1对变量进行自增操作,线程1先读取了变量inc的原始值,然后线程1被阻塞了;
  然后线程2对变量进行自增操作,线程2也去读取变量inc的原始值,由于线程1只是对变量inc进行读取操作,而没有对变量进行修改操作,所以不会导致线程2的工作内存中缓存变量inc的缓存行无效,所以线程2会直接去主存读取inc的值,发现inc的值时10,然后进行加1操作,并把11写入工作内存,最后写入主存。
  然后线程1接着进行加1操作,由于已经读取了inc的值,注意此时在线程1的工作内存中inc的值仍然为10,所以线程1对inc进行加1操作后inc的值为11,然后将11写入工作内存,最后写入主存。

我对这一段的理解,volidate关键字保证了变量被更新后立即被更新到主存中,并且将其他线程中的该变量副本标注为无效。这样其他线程在使用这个变量时需要重新去主存中获取最新的值。

这里要注意的时,无效后什么时候获取最新的值?下次访问相同内存地址的时候,这里的地址指的是主存中的地址。
那么再来看上面的问题:自增操作可以分解为3个操作:

  1. 将住内存的数据拷贝到工作内存中
  2. 将工作内存中的数据+1
  3. 将工作内存中的数据写会主内存。

如果线程1执行完第1步后阻塞,线程2执行完全部的3步,那么此时线程1内的数据已经被表明为无效了。无效后是不是立刻就获取主存中的值呢?并不是。下次访问相同内存地址的时候才获取最新的值,上面3步中第一步访问主存,第二步不访问主存,第三步访问主存。所以当线程1执行完第一步阻塞后,虽然此时数据已经无效了,但是因为第二步不访问主存,所以并没有更新工作内存的数据。所以错误就产生了。

或者大家也可以这么理解,volatile可以保证:

  1. 读取该变量时,不会从工作内存读取,而是直接从主存中读取
  2. 更新该变量时,会将更新后的值立刻写入到主存中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值