java volatile关键字
前言:
volatile用在并发编程中,主要用来解决缓存一致性。
1.内存模型
程序在执行时,临时数据都是放入内存中的,cpu执行很快,要比在内存中取数据,写数据快很多,所以cpu里面有了高速缓存,实际程序在执行时,先将数据从内存复制到cpu告诉缓存中,cpu在执行过程中,就在告诉缓存cache中读取和写入数据,然后再刷新到主存中。在高并发情况下,就会出现问题,每个线程在运行时有自己的告诉缓存,cache中数据修改了,没来得及同步到内存,其他线程在内存中读取了数据,就造成了脏读,出现了缓存与主存不一致情况。下面图的两种方案时硬件上实现的:
2.并发编程三个概念
1.原子性
一个操作或者多个操作,要么全部执行并且执行过程中不被任何因素打断,要么全部失败都不执行。
2.可见性
多个线程同时访问同一个数据,一个线程改变了这个数据,其它线程能够立即看到修改后的值
3.有序性
程序执行的顺序按照代码的先后顺序执行,cpu不会进行指令重排序。
指令重排序:处理器为了提高程序运行效率,它不保证语句的执行顺序和代码中一样,但是会保证执行的结果是一致的。 例如下面四条语句执行顺序是不确定的,顺序可能不一样,但是cpu会保证结果一样,在进行重排序时会考虑数据之间依赖性。
int i = 0;
int j = 1;
double tem = 10.1;
char a = 'a';
因此,想要并发程序正确的执行,必须要保证原子性、可见性、有序性。只要有一个没有保证,程序就会运行不正确。
java内存模型:
Java内存模型规定,所有的变量都是存在主存中,每个线程有自己的工作内存,线程对变量的所有操作都必须在工作内存中完成,而不能直接对主存进行操作,并且每个线程都不能访问其他线程的工作内存。
1.原子性 在java中,对基本的变量的读取和赋值操作时原子性的,这些操作不可被中断,要么执行,要么不执行。
x=1; 原子性
a=b;不是原子性,需要先读取b,再赋值a
如果要进行多个操作原子性的,可以通过synchronized和lock实现。
2.可见性 java提供了volatile关键字来保证可见性。当一个变量被volatile修饰时,它的修改会立即保存到主存中,并通知其它线程读取的数据无效,重新在内存中读取,
3.有序性 java通过volatile关键字来保证一定的有序性,volatile修饰的变量实际上是一个指令重排序的分界线,上部分代码和下部分代码都进行各自的指令重排序,但是上部分代码执行完了之后才会执行下部分代码。
volatile关键字:保证了有序性和可见性
1.可见性
下面这段代码,两个线程,当线程2把stop置为true时,线程1停止,但是,线程2把stop在自己的工作内存中置为true,又去干别的事情了,没来得及同步到主存,那这段代码就有问题了,线程1还会继续执行。所以stop变量应该用volatile修饰
//线程1
boolean stop = false;
while(!stop){
doSomething();
}
//线程2
stop = true;
2有序性 volatile的有序性是相对的,这个变量前面的代码先执行,然后这个变量后面的代码再执行,只能保证前面代码优于后面代码执行。但是前面代码可以进行自己的重排序,后面代码也可以进行自己的执行重排序。
3volatile不能保证对变量的操作是原子性的
package com.xhx.java;
/**
* xuhaixing
* 2018/8/11 22:03
**/
public class App {
private volatile int i = 0;
public void testIncrease(){
i++;
}
public static void main(String[] args) {
final App app = new App();
for(int i = 0;i<10;i++){
new Thread(()->{
for (int j = 0;j<1000;j++){
app.testIncrease();
}
System.out.println(app.i);
}).start();
}
}
}
看执行结果,volatile虽然保证了可见性,但是i++这个操作不具备原子性特征,读取变量值,+1,赋值,,很多线程可能都+1了或者已经读取了变量值正准备+1,在这个空隙之间,其他线程进行了i的赋值,这样有的线程+1得到的值就会和目前内存中的值一样,而没有真正的+1,, volatile对变量的操作不具备原子性。
要保证原子性可以用synchronized,lock或者atomic实现。
volatile使用场景:
由于volatile不具备原子性,所以要摆脱原子性,才能使用volatile
1.标记状态变量,使状态变量修改后立即生效
volatile boolean flag = false;
while(!flag){
doSomething();
}
public void setFlag() {
flag = true;
}
2.双重检查
保证对象赋值后立即生效。
class Singleton{
private volatile static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(instance==null) {
synchronized (Singleton.class) {
if(instance==null)
instance = new Singleton();
}
}
return instance;
}
}
上一篇: 中文Access2000速成教程--1.1 使用“向导”设计数据库
下一篇: JAVA内存模型