1.Volatile和JMM内存模型的可见性

谈谈对Volatile的理解

  • volatile是JVM提供的轻量级的同步机制

volatile有三大特性

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排

JMM是什么

  • JMM(Java内存模型,简称JMM)本身是一种抽象的概念并不真实存在
  • 由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存,工作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储到主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作必须在工作内存中进行。
  • 首先要将变量从主内存拷贝到自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成。

数据传输速率:硬盘 < 内存 < < cache < CPU

上面提到的概念 主内存 和 工作内存:

  • 主内存:主要包括本地方法区
  • 工作内存:属于该线程私有的栈和对主存部分变量拷贝的寄存器(包括程序计数器PC和cup工作的高速缓存区)

JMM的特性

JMM的三大特性:可见性,原子性,有序性

volatile只保证了两个,即可见性和有序性,不满足原子性

可见性(及时通知)

  • 指的是当主内存区域中的值被某个线程写入更改后,其它线程会马上知晓更改后的值,并重新得到更改后的值。
  • 例如:有三个线程往主线程修改变量age=20,三个线程各自变量拷贝一份,其中一个线程在自己的工作空间将20改成了25,然后将工作内存中的值,写入主内存,主内存中的值被修改后,其他工作内存中的值会马上获得通知。
用volatile验证可见性

我们对于成员变量没有添加任何修饰时,是无法感知其它线程修改后的值

class MyData {
    // 定义int变量
    int number = 0;

    // 添加方法把变量 修改为 60
    public void addTo60() {
        this.number = 60;
    }
}
public class Test {
    public static void main(String[] args) {
        // 资源类
        MyData myData = new MyData();

        // 用lambda表达式创建线程
        new Thread(() -> {
            System.out.println("线程进来了");

            // 线程睡眠三秒,假设在进行运算
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 修改number的值
            myData.addTo60();

            // 输出修改后的值
            System.out.println("线程更新了number的值为" + myData.number);

        }).start();

        // main线程就一直在这里等待循环,直到number的值不等于零
        while (myData.number == 0) {

        }

        System.out.println("main方法结束了");
    }
}

最后线程没有停止,没有输出 main方法结束了 这句话,说明没有用volatile修饰的变量,是没有可见性的。

当我们给变量添加volatile关键字修饰:volatile int number = 0,发现可以成功输出结束语句。

volatile 修饰的关键字,是为了增加 主线程和线程之间的可见性,只要有一个线程修改了内存中的值,其它线程也能马上感知,是具备JVM轻量级同步机制的。

你可能感兴趣的