java并发编程-volatile关键字

并发编程

MESI协议

基于失效的缓存一致性协议

有四个状态:

  • Exclusive(E):独享,其他缓存都没有有效的副本
  • Shared(S):共享,其他缓存都有有效的副本
  • Invalid(I):失效
  • Modified(M):修改

原子性、有序性、可见性

原子性

在java中,对基本数据类型的变量读取和赋值操作是原子性操作,这些操作是不可中断的,也就是要么读,要么不读,或者要么写要么不写。

1
2
3
4
5
x = 10; // 操作1
y = x; // 操作2
x++; // 操作3
x = x + 1;//操作4

以上的操作中只有操作1属于原子性操作。

有序性

有以下这样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int x = 10;
//volatile int x = 10; 如果x有volatile关键字修饰时,就可以保证有序性
void cpu0(){

x = 100;// 此时x的状态变幻情况为:S(共享) -> M(修改),此时通过cpu0会通知其他cpu的x的值为I(失效),再取出最新值,会消耗一段时间才会将x的值修改完成
flag = true;// 当flag此时是E状态时,变化状态:E(独享) -> M(修改),此时flag的值会直接修改,并发送给其他cpu状态置为S(共享)
}

void cpu1(){
while(flag) {
System.out.println(x == 100);
}
}

当有volatile关键字修饰时,会看到在程序运行时的汇编指令中会发现有locak指令,相当于一个内存屏障,会强制把缓存的操作写入主存。

当不能保证有序性时,打印输出的结果可能为false。所以在程序运行必须保证有序性。

可见性

在Java中,提供了volatile关键保证可见性。
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
此外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

volatile、synchronized

volatile可以保证对变量操作的有序性、可见性,但是不能保证原子性

synchronized可以保证对变量的有序性、可见性、原子性