java并发和高并发之线程安全性——可见性
一、可见性:
1、定义:线程对主内存的修改可以及时地被其他线程观察到;
2、导致共享变量在线程间不可见的可能性原因:
》线程交叉执行;
》重排序结合线程交叉执行;
》共享变量更新后的值没有在工作内存与主存间及时刷新;
3、不同解决方案对可见性的处理:
1)可见性——synchronized
JMM 关于synchronized的两条规定:
》线程解锁前,必须把共享变量的最新值刷新到主内存;
》线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量是需要从主内存中重新读取最新的值(注意,加锁与解锁是同一把锁)
原子性演示时,修饰的对象不同时,是互相不影响的。
2)可见性——volatile:
通过加入内存屏障和禁止重排序优化来实现,这种机制实现在CPU的指令层面,。
》对volatile变量写操作时,会在写操作后加入一条store屏障指令,将本地内存中的共享变量值刷新到主内存;
》对volatile变量读操作时,会在读操作前加入一条load屏障指令,从主内存中读取共享变量;
4、实例测试:
如果使用volatile修饰之前的计数器的值,是否可以获取正确的结果呢?
运行结果:
如上多次运行可发现,即使上方用volatile修饰,但是仍然无法保证结果,为什么呢?
因为add 方法中,实际执行了三步,在多个线程中执行时,出现问题,也证明volatile 这个关键字不具备原子性,
那volatile不适合如上计数场景,那volatile适合什么样的场景呢?
使用volatile必须具备两个特点:
1、对变量的写操作不依赖于当前值;
2、该变量没有包含在具有其他变量的不变的式子中。
常见使用方案:将volatile修饰的变量作为一个标志性变量,和双重检查机制的方案中。