volatile关键字⭐️通过案例分析与Synchronized的区别以及功能

目录

前言

一、volatile的特点

1.线程间可见性

1.1、案例

1.2 案例分析

2、禁止指令重排

解决方案

 章末


前言

        小伙伴们大家好,上次分析了Synchronized关键字的特点及使用方式,多线程中经常提到的除了这个还有volatile,来分析该关键字的特点

一、volatile的特点

1.线程间可见性

        用volatile声明的共享变量,保证了某个线程修改了该变量的值,新值对其他线程来说是立即可见的,因为volatile关键字强制将修改后的值立即写入内存。

1.1、案例

        在main方法里,新建一个线程休眠100ms,然后修改stop变量为true,与此同时通过countNum方法计数,直到stop变量为true停止,结果如下,一直循环表明检测不到stop变为true

public class VolatileTest {

        static boolean stop = false;
        public static void main(String[] args) {
            new Thread(() -> {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                stop = true;
                System.out.println("change stop to true...");
            }).start();
            countNum();
        }
        static void countNum() {
            int i = 0;
            while (!stop) {
                i++;
            }
            System.out.println("stopped... successfully at :"+ i);
        }

}

volatile关键字⭐️通过案例分析与Synchronized的区别以及功能_第1张图片

1.2 案例分析

        解决方法之一就是使用volatile关键字来声明变量,让其他线程可见,在stop修改后第一时间获取,修改后的结果如下,在i变量值为218467224的时候该线程检测到stop的值为true,跳出循环并打印;

static volatile boolean stop = false;

volatile关键字⭐️通过案例分析与Synchronized的区别以及功能_第2张图片

2、禁止指令重排

        在编译器和处理器优化指令执行顺序时,为了提高程序性能,可能会对指令进行重排。然而,在多线程环境下,指令重排可能会导致结果的错误或者不符合预期。使用volatile关键字修饰的变量,在读写操作前后会加入特定的内存屏障(memory barrier),这个内存屏障可以防止指令重排,确保操作的顺序符合程序员的预期。

volatile关键字⭐️通过案例分析与Synchronized的区别以及功能_第3张图片

说人话(bushi),具体来说,volatile关键字禁止的是以下三种类型的指令重排:

  • 在volatile写操作之前的读操作不能被重排到volatile写操作之后。
  • 在volatile写操作之后的写操作不能被重排到volatile写操作之前。
  • 在volatile读操作之后的读操作不能被重排到volatile读操作之前。
//伪代码举个例子

int x;
int y;

方法1(){
    x=1;
    y=1;
}

方法2(result r){
    r.r1 = y;
    r.r2 = x;
    }
在去获取上面的结果的时候,有可能会出现 4 种情况
  • 情况一:先执行方法2获取结果--->0,0(正常)
  • 情况二:先执行方法1中的第一行代码,然后执行方法2获取结果--->0,1(正常)
  • 情况三:先执行方法1中所有代码,然后执行方法2获取结果--->1,1(正常)
  • 情况四:先执行方法1中第二行代码,然后执行方法2获取结果--->1,0(发生了指令重排序,影响结果)
解决方案

        在变量上添加volatile,禁止指令重排序,则可以解决问题 ,注意只能将volatile添加到y变量上,因为关键字添加的屏障如下

volatile关键字⭐️通过案例分析与Synchronized的区别以及功能_第4张图片                   volatile关键字⭐️通过案例分析与Synchronized的区别以及功能_第5张图片

                        volatile 加在 y上                                                   volatile加在x上

加在x上显然是不行的,主要是因为下面两个原则:
  • 写操作加的屏障是阻止上方其它写操作越过屏障排到volatile变量写之下
  • 读操作加的屏障是阻止下方其它读操作越过屏障排到volatile变量读之上,因为此时y是正常变量也因为volatile声明了x导致y不可读

但是不用慌,有问题就会有小妙招

  • 写变量让volatile修饰的变量的在代码最后位置
  • 读变量让volatile修饰的变量的在代码最开始位置

 章末

好了,以上就是全部内容了,下次来分析分析其他的锁(什么?还有高手)


你可能感兴趣的:(Java多线程,java,jvm,开发语言)