JUC学习--volatile关键字&内存可见性

学习记录,若存在错误,请指出,谢谢!

首先,介绍一些内存可见性

内存可见性:当多个线程操作共享数据时,彼此不可见

为什么会导致这个情况?

在线程运行的过程中,JVM或者说是内存会为每一个线程分配一个独立的缓存用于提高效率。

举个例子:现在有一个读操作的线程和一个写操作的线程,对主存中的一个共享变量进行操作
写操作:线程先从主存中将共享变量读到线程独有的缓存中,然后对变量的值进行修改,最后再把修改后的变量的值写回到主存中。
读操作:读取主存中共享变量的值
但是,

读线程可能在写线程把修改的变量的值写回主存之前,就从主存中读了变量的值,但是这个值是原先的值

public class TestVolatile {

    public static void main(String[] args){

        ThreadDemo threadDemo = new ThreadDemo();
        new Thread(threadDemo).start();

        while(true){
          
                if(threadDemo.isFlag()){
                    System.out.println("--------");
                    break;
                }
            
        }
    }

}

class ThreadDemo implements Runnable{

    private volatile boolean flag = false;

    @Override
    public void run() {
        try {
            Thread.sleep(2000);
        }catch (InterruptedException e){
        }

        flag = true;
        System.out.println("flag = " + flag);
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

控制台打印的结果:

《JUC学习--volatile关键字&内存可见性》

这就导致了一个问题,写线程已经把flag的值修改成了true,但是读线程(也就是代码中的main线程)并没有输出“——-”。

于是,我做了以下的修改

public class TestVolatile {

    public static void main(String[] args){

        ThreadDemo threadDemo = new ThreadDemo();
        new Thread(threadDemo).start();

        while(true){
            synchronized (threadDemo){
                if(threadDemo.isFlag()){
                    System.out.println("--------");
                    break;
                }
            }
        }
    }

}

在main线程中加上了synchronized关键字。于是

《JUC学习--volatile关键字&内存可见性》

但是,使用了synchronize关键字之后,会导致程序的效率非常的低下。假设程序中有多个线程,每次访问synchronized()这行代码的时候,总是要判断是否有别的线程占有锁,如果有,当前的线程就只能阻塞,直到CPU下次空闲的时候才可能执行。

为了保证程序的效率,就不能够加锁,但是又要解决内存可见性的问题,就要提到volatile关键字了。
volatile关键字:当多个线程对共享数据进行操作时,可以保证该共享数据是内存可见的。
原因是:volatile使用了计算机底层的内存栅栏,可以将线程独立缓存中的值实时更新到主存中去,我们可以理解为,直接对主存进行操作。

当然,使用了volatile关键字也会对系统的性能有一点损耗,因为每进行一次操作,主存中的内容也会相应改变,但是效率比加锁还是要高得多的。

private volatile boolean flag = false;

将共享变量flag用volatile关键字修饰。

《JUC学习--volatile关键字&内存可见性》

但是,volatile并不能直接替代synchronized关键字,因为相较于synchronized而言,volatile只是一种较为轻量级的同步策略

注意:
volatile不具备“互斥性”;

volatile不能保证“原子性”;

若要满足“互斥性”和“原子性”,则不能使用volatile

    原文作者:JUC
    原文地址: https://blog.csdn.net/dyt443733328/article/details/79642064
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞