锋盈数科-知识库 Logo
首页
软件开发
计算机基础
Hello Halo
新手必读
关于本知识库
登录 →
锋盈数科-知识库 Logo
首页 软件开发 计算机基础 Hello Halo 新手必读 关于本知识库
登录
  1. 首页
  2. 默认分类
  3. volatile关键字的原理和要避免的误区

volatile关键字的原理和要避免的误区

0
  • 默认分类
  • 发布于 2024-09-24
  • 11 次阅读
黄健
黄健

背景

    最近做code review看到有的同学在承载缓存数据的变量里加了volatile关键字。想起来之前项目中也看到有的同学习惯在从配置中心获取的配置数据的变量上加volatile。今天就来探讨一下这个volatile加的有没有必要。

volatile关键字的作用

1>防止指令重排

2>禁用工作内存缓冲区,直接使用主内存。


经典使用场景

场景1

public static Singleton getInstance() {
    //第一次null检查     
    if (instance == null) {
        synchronized (Singleton.class) {    //1     
            //第二次null检查       
            if (instance == null) {          //2  
                instance = new Singleton();//3  
            }
        }
    }
    return instance;
}

如果不用volatile,则因为内存模型允许所谓的“无序写入”,可能导致失败。某个线程可能会获得一个未完全初始化的实例。

场景2

private volatile int value;
//读操作,没有synchronized,提高性能
public int getValue() {
    return value;
}

//写操作,必须synchronized。因为x++不是原子操作
public synchronized int increment() {
    return value++;
}

这段代码,可实现一个线程间安全的计数器。因为加了valatile关键字。每次线程都能取到最新值做加减。


要避免的误区

在代码评审的时候看到volatile被滥用的情况。说说我个人的看法:很少变化,对时间不是特别敏感的情况下不建议用volatile关键字。

举个例子:从公司的配置中心取到一个配置数据。不建议用volatile。

一般来说配置中心的架构是下面这个样子

图片

一条数据从用户变更到集中存储的配置中心,配置中心下发到真正使用的机器上,之前公司是要经过90s(客户端90s为周期定时去配置中心取最新数据)。

加了volatile关键字在这种场景只是能更快的看到这个最新值而已。下面我们来测试下这个【更快】有多久。

public class VolatileTest {
    private boolean endRun = false;
    @Test
    public void noVolatile() throws Exception {
        Runnable r1 = new Runnable() {
            public void run() {
                int i = 0;
                while (!endRun) {
                    System.out.println("I am still running" + i++);
                }
            }
        };
        Runnable r2 = new Runnable() {
            public void run() {
                endRun = true;
            }
        };
        new Thread(r1).start();
        new Thread(r2).start();
        Thread.sleep(9000);
        System.out.println("end run");
    }
}

这个代码里,在第一个线程使用endRun这个变量。第二个线程去改变endRun这个变量的值。一旦第一个线程看到了第二个线程的值的变化,就会马上停止循环。

运行结果如下:

说明经过了两个循环的时间,线程就读到了另外一个线程变化的值。对照下面的时间延迟表,我们来计算下:


平均执行一行简单代码要执行5个指令。如上执行一个指令需要1ns。每次循环执行2行代码,从运行结果来看共执行了2次。共5*1*2*2=20ns。实际数据应该不是如此,而且是变化的。但是应该都是ns级别的。

相比较90s的可见性延迟,ns级别可以忽略不计。

再看看为了早ns级看到结果,所花费的开销:volatile关键字本质是让L1缓存、L2缓存这种cpu缓存失效了,直接主存访问。如果要访问的字段在L1缓存里,从配置中心取的数据1天变化一次。以字段放在L2缓存为例。加了volatile关键字,访问时间要从4ns上升到100ns,如果这个变量每个请求都要访问,每秒QPS是1000。则1天为了取这个数据将多花1*24*3600*1000*(100-4)ns约等于8300ms。

相比获得的收益来讲,代价要大出好几个数量级。但是本身的时间开销本来就很小,坦白说一般的系统一天多花个8.3s也是可以接受的。但是这样的变量多了,也是个不小的负担。而且这个负担会随着系统压力增加而加重。

定时将缓存加载到内存原理相同,不建议使用volatile。


其他引申思考

上面结论是在对时间敏感度不高的情况下不建议用volatile,但是对于一般的系统,用了对系统的影响也还好。最怕的事情是做了一个实际上意义并不大,却引入系统风险的优化。

上周我review同学的代码,这个同学是个有技术追求的同学。喜欢写代码的时候进行些小优化。这是个好习惯。但是对review代码的人要求很高。因为普通的业务逻辑影响的就是那么一块范围内的。但是优化却可能会影响其他部分。

这次他在好几处,把原来打印更新缓存的日志里,原来只打印:【更新缓存成功】的地方加了个【更新缓存成功,影响数据XX条】。这个XX取的是guava Cache的.size()方法。

这段代码我仔细看了guava cache的初始化方法,这个初始化方法非常复杂,里面用了几处断言(不能为空)。这个初始化方法没有统一的try catch捕获异常,一旦有地方抛出异常。有可能会没有完全实例化。我把自己的这个想法提了出来。他通过代码走查梳理向我证明了,确实不会有空指针的情况。我同意他是对的。但是这整个过程说明了我对这件事情的谨慎。如果我们上线了一个功能,功能有问题,新功能上线会有灰度、观察期慢慢上量的过程,影响不会多大。但是改了其他部分,特别是感觉绝对不会有问题的部分。如果出现问题了,自己团队的信任分一下子就会降下来了。以后再进行变更,需要反复证明影响,非常被动。

原文链接:https://mp.weixin.qq.com/s/hWvbWNXRe4rRKEoEUlrzJQ

标签: #知识库 257
相关文章
最全的办公楼智能化解决方案

最全的办公楼智能化解决方案 2024-10-16 08:40

办公楼综合体智能化如何建设?有哪些系统?近几年,办公楼智能化的项目越来越多,不少项目经理都参与其它,同事办公楼综合体也是弱电系统涉及的最多的项目之一,本期我们一起来看下,最全的办公楼项目智能化设计方案。

规范标准查询、下载网站 2024-10-12 16:41

我们在工作中经常需要用到各种各样的规范标准,这里给大家介绍一些免费查询和下载规范的网站,个人亲测可用。 标准查找查新网站 工标网: http://www.csres.com/ 中国国家标准化管理委员会:http://openstd.samr.gov.cn/bzgk/gb/index 全国标准信息公共

【计算机网络】网络层协议解析 2024-10-08 11:24

网络层的两种服务 IPv4 * 分类编址 划分子网 无分类地址 IPv4地址应用 IP数据报的发送和转发过程 * 主机发送IP数据报 路由器转发IP数据报 IPv4数据报首部格式 ICMP网际控制报文协议 虚拟专用网VPN与

FFmpeg教程(超级详细版) 2024-10-08 11:24

一、参考资料 通过ffmpeg把图片转换成视频 FFmpeg命令(一)、使用filter_complex命令拼接视频 FFmpeg 视频处理入门教程给新手的 20 多个 FFmpeg 命令示例 FFmpeg命令行转码

计算机网络:物理层 —— 数据的传输方式 2024-10-08 11:24

文章目录 * 传输方式 * 串行传输 * 串行传输方式 特点 应用 并行传输 * 特点 应用 网卡的串/并转换 同步传输 * 同步时钟频率的误差问题 特点 应用<

授权码机制 V2.1 2024-10-07 10:26

大家好,我是机灵鹤。 根据读者朋友们反馈的问题和建议,对 授权码 V2.0 版本做了一些优化。 优化内容主要解决了以下几个问题: 优化了授权机制中的时间校验逻辑,避免用户通过回调本地时间来绕过授权机制的问题。 封装和简化了授权接口,开发者可以更方便地接入到自己的程序中。

目录

IT 外包服务商

  • 意见投递
  • zyf6619

软件开发应用

主菜单

  • 首页
  • 软件开发
  • 计算机基础
  • Hello Halo
  • 新手必读
  • 关于本知识库
Copyright © 2024 your company All Rights Reserved. Powered by Halo.