volatile特性
volatile的特性:
1. 保证可见性
2. 不保证原子性
3. 禁止指令重排
- 可见性
可见性:线程变量可以同步主存的共享变量
可见性问题:线程和cpu不是在同一内存中工作,cpu的内存叫做主存,线程的内存叫做工作内存。当线程中某个变量发生了变化,就会同步到主存中,没有变化的就不会同步。可以发现有个缺陷,当线程A和线程B同时调用同一个变量,线程A中没有对这个变量进行操作,而线程B对这个变量进行了操作,并且同步到了主存中。此时会发生一个问题,就是线程A不知道该变量已经被修改。这就产生了可见性问题。
package com.luoKing.Dvolatile;
import java.util.concurrent.TimeUnit;
public class seeyou {
static int num = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (num==0){
}
}).start();
TimeUnit.SECONDS.sleep(1);
num++;
System.out.println(num);
}
}
我们可以发现程序发生阻塞。
如何解决这个问题呢?
num用volatile修饰,因为volatile可以保证可见性
package com.luoKing.Dvolatile;
import java.util.concurrent.TimeUnit;
public class seeyou {
static volatile int num = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (num==0){
}
}).start();
TimeUnit.SECONDS.sleep(1);
num++;
System.out.println(num);
}
}
2.不保证原子性
什么是原子性?
原子,组成物质的最小单位,不可再分。原子性操作:不可中断,要么全部执行成功,要么全部执行失败。不能被其他线程干扰。
package com.luoKing.Dvolatile;
public class unatomic {
private volatile static int num;
public static int add(){
return num++;
}
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
for (int i1 = 0; i1 < 2000; i1++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){//mian线程和gc线程在有其他线程运行的时候,礼让线程,不执行
Thread.yield();
}
System.out.println("num = " + num);
}
}
结果
num = 17188
可见volatile并不可以保证原子性
- 禁止指令重排
指令重排:我们写的程序,在计算机中执行,并不是按照我们程序的顺序执行,他会重写编排,以达到的最优的性能,但这也会导致运行的结果和我们期望的结果有偏差
加了volatile关键字,在cpu中会产生一个叫做内存屏障的东西,会阻止指令重排
作用:
- 保证程序按照特定的顺序操作
- 可以保证某些变量的内存可见性