可见性通过volitile保证随笔

可见性:

可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。要了解多线程并发中的线程可见性, 首先需要了解下JMM,见图


JMM模型

java内存模型中定义了8中操作都是原子的,不可再分的。

lock(锁定):作用于主内存中的变量,它把一个变量标识为一个线程独占的状态;

unlock(解锁):作用于主内存中的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定

read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便后面的load动作使用;

load(载入):作用于工作内存中的变量,它把read操作从主内存中得到的变量值放入工作内存中的变量副本

use(使用):作用于工作内存中的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作;

assign(赋值):作用于工作内存中的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作;

store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送给主内存中以便随后的write操作使用;

write(操作):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。

当在线程中读取一个变量的时候,首先将变量从系统空间经过 read和load 读取并加载到用户内存,然后对其进行使用和赋值,最后再将操作后的数据 经过 store和write 同步到系统内存。

一般的共享变量在被多线程操作时由于各种原因,更多的是对变量副本进行操作,并只是在用户内存(寄存器)中存储,并不能及时的将数据同步到系统内存,另外,读取数据时也存在一定的可能性直接读取用户空间数据,对其进行操作,这样,操作的结果就不能及时的被其它用户线程所同步到。线程可见性也就无法得到保证,下图是做的例子


存在可见性问题demo
存在可见性问题demo

1,开始启动一个线程(thread1)判断是否每次都能读到最新的count值

2,一秒后,启动另一个线程(thread2)对变量进行相反值赋值操作

3,结果thread1中count值每一次都一样,导致线程无法结束

结论:共享变量count存在可见性问题

要解决可见性问题,就可以用volatile关键字修饰共享变量。效果如图


volatile修饰共享变量

由图可知,与上面实例的区别只是将volatile修饰了count。但执行结果却大相径庭,thread1即时终止了,说明thread2中的count值被修改后及时同步到了系统内存,而每次count值也都是从系统内存读取,从而保证了该共享变量的可见性。

其实,volatile 主要是通过内存屏障来体现其可见性的。

内存屏障分为两种:Load Barrier 和 Store Barrier

Load Barrier可以让用户内存中的数据失效,强制从系统内存加载数据;

Store Barrier让写入用户内存中的共享变量值及时更新写入系统内存,以便让其他线程可见。

这样就保证共享变量的可见性,volatile限制重排序的作用,也是其一大特性,但是这里对有序性不做过多分析,一笔带过。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,376评论 11 349
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,145评论 1 32
  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,646评论 8 265
  • 以上代码会重复运行 , 不会停止。 JMM(java内存模型) 若想学习好多线程, 那么必须了解一下JMM Jav...
    尼尔君阅读 1,766评论 0 2
  • 在正常情况下,啥都不做会给人带来精神上的极度无聊和懈怠、以及延伸出的对自己人生价值的否定甚至更多的精神问题。那种痛...
    快活糖果阅读 106评论 0 0