背景:Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了同步块和volatile关键字机制。
Java内存模型
Java内存模型规定了所有的变量都存储在主存中,每条线程中有自己的工作内存,线程中的工作内存中保存了该线程所使用的变量(这些变量是从主存中拷贝而来)
线程对变量的所有操作都必须在工作内存中进行。不同线程之间无法直接访问对方工作内存中变量,线程变量值的传递均需要通过主内存来完成。
如图:Java内存模型:
Java并发编程三点要求:
- 原子性
- 有序性
- 可见性
一.原子性
二.有序性
定义:即程序执行随着代码顺序而执行。
但是在程序执行的时候,为了提高执行效率,虚拟机会进行指令重排序已追求更高效率。重排序对单线程没有影响,也不会影响最终的结果,但其对多线程环境下的结果可能会产生影响,所以为了确保最终结果的正确性,我们就需要禁止重排序。
解决办法:JVM中对Java代码有个happened-before原则和使用volatile关键字确保了不会进行指令重排序继而保证有序性。
三.可见性
定义:当多个线程去访问同一个变量时,某个线程对变量执行修改后,其余线程会立刻看到修改的变量。
概述:
例如:
线程1执行的代码:
Int i=0;
I=10;
线程2执行的代码:
J=i;
结果为:J=0;
解决:当一个共享变量被volatile修饰时,他会保证修改的值立即被更新到主存,当有其他线程需要读取时,他会去主存中读取新值。
另外还可以通过synchronized和lock能保证同一时刻只用一个线程获取锁然后执行同步代码,并且在释放锁之前会对变量的修改刷新到主存中,因此可以保证可见性。
总结;
volatile可以保证线程可见性且提供了一定的有序性,但是无法保证原子性。在jvm底层volatile是采用“内存屏障”来实现的。
一旦一个共享变量(类的成员变量,类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
保证了不同线程对这个变量进行操作时的可见性。
禁止进行指令重排序-----保证了有序性
注意:volatile不能保证原子性。