线程不安全之volatile关键字
文章目录
线程不安全之volatile关键字
上篇博文讲了线程不安全问题
https://editor.csdn.net/md/?articleId=111461834
可见性
- 就是说, 在多线程的环境下,某个共享变量A被其中一个线程修改了,其他线程就会知道有一个线程修改了这个共享变量A,然后其他线程若是要使用这个共享变量,就会直接去内存中去读取整个共享变量,而不是从自己的空间中去读取这个变量A。(线程自己的内存区域中存储着这个变量A的缓存地址,当他发现有别的线程去修改变量A的时候,就会强制让自己内存区域中的这个变量A的缓存地址无效,然后去从主存中去访问这个变量A,缓存和主存的内容是一致的)
- 被volatile申明的变量就具有可见性的性质
有序性
-
在我们基础的理解上 Java的代码执行是顺序执行的,但是在虚拟机里代码可能会进行重排序
int a = 1; //1 int b = 1; //2 a++; //3 b++; //4
上面四行代码,无论先执行第三行 还是第四行 都不会最终对a的值和b的值产生影响,所以在虚拟机执行这些代码的时候就可能进行重排序。但是如果第三行代码需要执行的时候需要消耗5ms,但第四行只需要消耗1ms,所以当然就会先执行第四行。
-
但是在多线程里面就会可能出现问题了
boolean static ready = false; int static x = 0; Thread t = new Thread(new Runnable() { @Override public void run(){ if (ready) { t++; } } }); t.start; ready = true; // 1 System.out.println(x); // 2
如果第1和第2发生重排序的话就会发生线程不安全问题。
但是 volatile 修饰的关键字只能让自己本身不发生重排序。也就是说我只呆在自己的位置,并且让我后面的不去前面 ,前面的不去后面,起到一个屏障的作用。 但是无法阻碍前面的和前面(后面的和后面的)被重排序。
分析一下单例模式
public class lesson4 {
private static volatile lesson4 LS= null;
// 双重校验锁
public void getLesson() {
if (LS == null) { // 1
synchronized (lesson4.class) { // 2
if (LS == null) { // 3
LS = new lesson4(); // 4
}
}// 5
}
}
}
假设有线程 A 和 线程 B 同时去执行 getLesson() 方法
-
第一种可能就是 A B两个 线程都执行到了 第2行代码 然后去抢占锁,假设A线程抢占锁成功,然后去执行3 4 行代码,最后执行第五行代码代码也就是释放锁之前,B线程一直就被卡在了第二行代码。但是当他获取锁之后,发现LS != null ,volatile关键字让他从内存中看见了了LS不为null,已经被new了,然后B线程就释放锁。
-
synchronized 保证了操作是原子的,也就是说当有一个线程去new LS的时候,其他线程是无法LS进行操作的,而第三行又保证了不会重复new
-
但是抢占锁这个操作是挺耗费cpu资源的,第1行的就是为了提高效率,若是已经new了一个LS,那木我们就可以直接判断不为null,不进去抢占锁了,减少了cpu资源的耗费
-
我们知道 new 一个对象在JVM虚拟机中可以分为四步走,第一就是类加载过程,知道这个类会耗费多少内存空间,第二步就是开辟内存空间,第三步就是在空间上创建这个对象,第四步就是把这个对象赋值给字段。 volatile 关键字保证初始化对象和对象引用的时候不会进行指令重排序。
本文地址:https://blog.csdn.net/shadow___h/article/details/112000485