volatile (易变的)

volatile是用于解决多线程下内存不可见的问题。

多线程在读取并操作它修饰的变量时候,读的时候都会去主存读取最新值,修改完马上会同步到主存。

但是volatile 变量的操作如果不是原子操作,不能保证线程同步。

原子操作:指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束。

java中的volatile_主存


Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。

volatile可以解决多线程内存不可见的问题。

当这个变量被标记成volatile时,这个变量就会直接从主内存获取更新,而不是从自己工作空间。

主内存和工作空间的交互

java中的volatile_主存_02

volatile可以通过插入内存屏障的方式,防止指令重排序。

As-If-Serial原则: 不管怎么进行指令重排序,单线程内程序的执行结果不能被改变。

Happens-Before原则: 用来指定两个操作之间的执行顺序,由于这个两个操作可以在一个线程之内,也可以在不同线程之间,通过这个规则可以保证跨线程的内存可见性。
具体规则如下:(具体手段是插入内存屏障)

1.程序次序规则:一个线程内,按照代码书写顺序,书写在前面的操作先发生于书写在后面的操作.

2.锁定规则:一个UNLOCK操作先行发生于后面对同一个锁的UNlock操作.

3.volatile变量规则:对volatile 修饰的变量的写操作 先行发生于后面对这个变量的读操作;

4.传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C;

5.线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作

6.线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生

7.线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行;

8.对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;

为什么Java中的long与double可能存在线程安全的问题

除了long和double类型,Java基本数据类型都是的简单读写都是原子的,而简单读写就是赋值和return语句。

目前的JVM(java虚拟机)都是将32位作为原子操作,并非64位。当线程把主存中的 long/double类型的值读到线程内存中时,可能是两次32位值的写操作,显而易见,如果几个线程同时操作,那么就可能会出现高低2个32位值出错的情况发生。
要在线程间共享long与double字段是,必须在synchronized中操作,或是声明为volatile。

java中的volatile_主存_03