volatile实现原理

Java内存模型一节,除了synchronized外,我们还提到一个常用关键词—-volatile,我们说过volatile保证了并发环境的可见性和顺序性,使用volatile修饰的变量,当然值发生改变时,可以同步到其他线程,其他线程按新值进行计算,下面我们编写代码来看下volatile关键词的使用和实现原理,验证我们前面抛出的结论。

当我们使用多线程对共享变量进行操作时,如果不用volatile,会是什么现象呢?以线程一节描述的使用标识位停止线程执行为例说明,代码如下:

public class VolatileTest {
    // 标志位
    boolean mInterrupted = false;
  
    // 设置标志位为true,打断线程运行
    public void needInterrupt(boolean isNeedInterrupt) {
        this.mInterrupted = isNeedInterrupt;
        System.out.println("修改mInterrupted值为:" + mInterrupted + ",time:" + System.currentTimeMillis());
    }

    public static void main(String[] args) {
        VolatileTest test = new VolatileTest();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "开始执行,time:" + System.currentTimeMillis());
            while (!test.mInterrupted) {
            }
            System.out.println(Thread.currentThread().getName() + "结束执行,time:" + System.currentTimeMillis());
        }, "CustomThread1").start();

        new Thread(() -> {
            try {
                Thread.sleep(2000);
                test.needInterrupt(true);
                System.out.println(Thread.currentThread().getName() + "打断关联的线程执行,time:" + System.currentTimeMillis());
            } catch (InterruptedException e) {
            }
        }, "CustomThread2").start();
    }
}

随后运行,输出如下:

volatile实现原理
1-4-11-1

可以看出在设置标志位为true后,很长一段时间仍未打印CustomThread1停止运行的日志,换而言之,我们虽然在CustomThread2中修改了mInterrupted的值为true,但是CustomThread1中值仍然为false,明显不符合我们预期,那么加上volatile修饰又会怎样呢?修改mInterrupted声明,添加volatile修饰,运行结果如下:

volatile实现原理
1-4-11-2

可以看出在使用volatile修饰后,在CustomThread2中执行打断后,CustomThread1立即停止运行了。

volatile实现原理

在synchronized实现原理中,我们了解到可以从字节码和汇编码两个角度去了解synchronized的实现,对于volatile,我们不妨也做相同假设,首先我们获取VolatileTest的字节码,看是否有相关实现,字节码代码如下:

volatile实现原理
1-4-11-3

从字节码可以看出,针对mInterrupted变量而言,除了声明时多了一个ACC_VOLATILE标记,在使用过程中无明显变化。

看来字节码中并没有volatile的核心实现,我们继续使用hsdis获取VolatileTest类的汇编码,进一步确定其实现,字节码如下:

volatile实现原理
1-4-11-4

可以看到在更新mInterrupted值时,增加了lock addl $0x0,(%rsp)指令,该指令就使得当前对共享变量的修改对其他线程立即生效,而lock addl是内核中的写屏障指令,所以我们一般说volatile关键词底层是通过内存屏障指令实现的。在有volatile修饰的变量需要赋值时,处理器会插入内存屏障指令,将当前线程工作内存的值刷新进主存,同时使得其他线程工作内存中引用的该变量的缓存地址失效,重新从主存同步新值,完成业务逻辑。

volatile与synchronized

volatile与synchronized区别见下表:

关键字 并发特性 作用级别 是否阻塞其他线程 字节码实现 汇编实现 备注
volatile 顺序性,可见性 变量 / lock addl /
synchronized 顺序性,可见性,原子性 变量,函数 monitorenter/monitorexit/ACC_SYNCHRONIZED lock cmpxchg /

原文始发于微信公众号(小海编码日记):volatile实现原理

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/67758.html

(0)

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!