线程并发的时候, 通常会遇到下面三种问题
1)原子性
2)可见性
3)有序性
在阅读这篇文章时,需要先了解以上三种概念,这里不做详细的说明,网上有各种说明,在此略过:
Volatile两大作用
1 .可见性
2. 防止重排(保证有序性)
1. Volatile可见性
直接用一段代码来看看并行会出现的问题
package com.yaya.java.volatileTest;
public class VolatileTest {
volatile int a = 1;
volatile int b = 2;
public void change() {
a = 3;
b = a;
}
public void print() {
if ((b == 3) && (a != 3)) {
System.out.println("b=" + b + ";a=" + a);
}
}
public static void main(String[] args) {
while (true) {
final VolatileTest test = new VolatileTest();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
test.change();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
test.print();
}
}).start();
}
}
}
看看结果诡异不
不考虑线程安全, a b 的值有下面几种可能
1. change先执行 结果为 a = 3, b =3
2. change 后执行, 结果为a=1, b=2
3. 交叉执行, 结果为 a= 3, b=2
但既然有个条件
为什么会打印 b=3, a=3 或者b=3, a=1的情况呢,这是因为上面的两个线程之间,没有实现可见性
if((b == 3) && (a !=3) 还会打印a=3, b=3 是因为 运行到 if((b == 3) && (a !=3)时,是因为 线程1运行完以后 自己工作内存中a=3, b=3但是并不一定什么时候写入物理内存中, 恰巧这个时候b=3写入物理内存,a=3没写入,所以物理内存 a=1 但是system.out.pritnln是, a=3已经写入物理内存,所以会出现a=3.b=3这样的结果
b=3, a=1 又是怎么出现的呢? 是因为 线程1运行完以后 内存中a=3, b=3但是并不一定什么时候写入物理内存中, 恰巧线程二读取的时候 b=3写入物理内存,a=3没写入,也就是说线程1修改的内存对于线程2并不可见, 可见性没有实现就是这个意思。
那这么确保可见性呢,用synchronized可以 但是synchronized只能修饰一个对象, 或者方法, 对于一个变量说来可以使用volatile, 因为使用volatile修饰以后就会强制在复制以后立即从工作内存中写入物理内存,不会出现上面这种诡异的现象了
2. Volatile的防止重排
对于指令重排这里只做简单的介绍:
指令重排序,一般来说,对于单线程来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的,但是并发下并不能保证最后的结果,使用Volatile禁止指令重排
volatile的原理和实现机制
下面这段话摘自《深入理解Java虚拟机》:
“观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”
Volatile并不能保证原子性,常见的原子性可以通过以下两种方式理解:
1) a++ 并不是一步就可以实现, 它分为以下几步:读取a的值 在a的值上+1, 将a的值写入主内存(物理内存)
2)b=a 和上面的a++理解一样, 读取a, 将a的值赋给b , 写入主内存(物理内存)
Volatile只能保证读取的时候从主内存中读取, 但是在+1的时候, 如果主内存的值变化, 就会造成数据的不一致, 所以不能保证数据的原子性,为了确保数据的一致性,Java 中提供Atomic包来实现原子性, 或者说Atomic是在Volatile的基础上, 再使用CAS方式实现了线程安全。关于Atomic包会在后续文章中将详细说明