volatile的可见性
通知OS(操作系统)底层,在CUP计算的过程中,都要检查内存中数据的有效性,保证最新的内存数据被使用
下面先看一段代码:
public class TestSync {
/* volatile */ boolean b = true;
void m() {
System.out.println("start");
while (b) { //死循环
}
System.out.println("end");
}
public static void main(String[] args) {
TestSync t = new TestSync();
new Thread(new Runnable() {
@Override
public void run() {
t.m(); //先启动一个线程调用 m()
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.b = false; //1 秒钟过后 将b 置为false 理想情况下会打印 end
//如果 将 volatile 关键字 注释掉 那么运行时 将只打印 start 而不打印end 程序在此处卡死(可能会让你的电脑cup沾满哦)
//加上 volatile 后 先打印start 1s后打印end
//原因: volatile 可见性通知OS操作系统底层,在CPU计算过程中,都要检查内存中数据的有效性。保证最新的内存数据被使用。
// CPU在计算时 ,会将一些数据缓存,这里的 b 就被缓存了, 每次计算时都直接取缓存中的数据,所以会一直取到true
//加上 volatile后 程序调用的时候 每次都会去内存(数据装载在内存中,也就时这个 b )中去看下数据是否有改变,保证取到最新的数据来计算
}
}
volatile 只保证可见性,不能保证原子性;不是加锁问题,只是内存数据可见
public class TestSync {
volatile int count = 0;
/*synchronized*/ void m(){
for(int i = 0; i < 10000; i++){
count++;
}
}
public static void main(String[] args) {
TestSync t = new TestSync();
List<Thread> threads = new ArrayList<>();
//创建了10个线程放到 list中
for(int i = 0; i < 10; i++){
threads.add(new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}));
}
//然后遍历启动线程
for(Thread thread : threads){
thread.start();
}
for(Thread thread : threads){
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//理论上 这里的 count 应该是 10* 10000 = 100000,但实际情况却少的多(在m2()方法不加 synchronized 关键字的时候)
//加上 synchronized 就是100000
//也就可以看出 volatile, 只能保证可见性,不能保证原子性。
System.out.println(t.count);
}
}