欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

线程不安全之volatile关键字

程序员文章站 2022-03-19 16:06:11
文章目录线程不安全之volatile关键字可见性有序性分析一下单例模式线程不安全之volatile关键字上篇博文讲了线程不安全问题https://editor.csdn.net/md/?articleId=111461834可见性就是说, 在多线程的环境下,某个共享变量A被其中一个线程修改了,其他线程就会知道有一个线程修改了这个共享变量A,然后其他线程若是要使用这个共享变量,就会直接去内存中去读取整个共享变量,而不是从自己的空间中去读取这个变量A。(线程自己的内存区域中存储着这个变量A的缓存地址...

线程不安全之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() 方法

  1. 第一种可能就是 A B两个 线程都执行到了 第2行代码 然后去抢占锁,假设A线程抢占锁成功,然后去执行3 4 行代码,最后执行第五行代码代码也就是释放锁之前,B线程一直就被卡在了第二行代码。但是当他获取锁之后,发现LS != null ,volatile关键字让他从内存中看见了了LS不为null,已经被new了,然后B线程就释放锁。

  2. synchronized 保证了操作是原子的,也就是说当有一个线程去new LS的时候,其他线程是无法LS进行操作的,而第三行又保证了不会重复new

  3. 但是抢占锁这个操作是挺耗费cpu资源的,第1行的就是为了提高效率,若是已经new了一个LS,那木我们就可以直接判断不为null,不进去抢占锁了,减少了cpu资源的耗费

  4. 我们知道 new 一个对象在JVM虚拟机中可以分为四步走,第一就是类加载过程,知道这个类会耗费多少内存空间,第二步就是开辟内存空间,第三步就是在空间上创建这个对象,第四步就是把这个对象赋值给字段。 volatile 关键字保证初始化对象和对象引用的时候不会进行指令重排序。

本文地址:https://blog.csdn.net/shadow___h/article/details/112000485

相关标签: 进程多线程