volatile
- 当写一个volatile变量时,jvm会把该线程对应的本地内存中的变量强制刷新到主内存中去;
- 这个写操作会导致其他线程中的缓存无效。
禁止指令重排序优化
重排序:
- 编译器优化的重排序(编译器优化)
- 指令级并行重排序(处理器优化)
- 内存系统的重排序(处理器优化)
- 当第二个操作是voaltile写时,无论第一个操作是什么,都不能进行重排序
- 当第一个操作是volatile读时,不管第二个操作是什么,都不能进行重排序
- 当第一个操作是volatile写时,第二个操作是volatile读时,不能进行重排序
volatile不能保证原子性
原子性:程序要么完整的被执行,要么完全不执行。
例子:
public class volatileTest {
private volatile int number=0;
public int getNumber() {
return this.number;
}
public void add() {
this.number++;
}
public static void main(String[] args) {
volatileTest v=new volatileTest();
for(int i=0;i<500;i++) {
new Thread(new Runnable() {
public void run(){
v.add();
}
}).start();;
}
//如果还有子线程在运行,主线程就让出CPU资源,直到所有的子线程都运行完,主线程再继续往下执行
while(Thread.activeCount()>1) {
Thread.yield();
}
System.out.println("number:"+v.getNumber());
}
}
运行结果:
number:500
number:499
number:498
为啥运行结果会不同呢???不应该都是500吗???
在本示例中,用volatile来保证了number变量在各线程之间的可见性,按照volatile的作用的确应该输出500。但是由于num++这个操作不具备原子性,它包含了三个操作:1)从内存中读取num的值;2)把num的值进行+1操作;3)把修改后的num重新写会内存中。各线程通过volatile无法保证进行操作的值是当前更新后的值,即可能存在两次+1操作的结果是相同的。
解决方案:
- 使用synchronized关键字
- 使用ReentrantLock(java.util.concurrent.locks包下)
- 使用AtomicInterger(java.util.concurrent.atomic包下)
volatile适合场所:
1.对变量的写入操作不依赖其当前值
- 不满足:num++,count=count*8等;
- 满足:boolean变量,记录温度变化的变量等
2.该变量没有包含在具有其他变量的不变式中
- 不满足:不变式low<up