• volatile型变量自增操作的隐患


      用FindBugs跑自己的项目,报出两处An increment to a volatile field isn’t atomic。对应报错的代码例如以下:

    volatile int num = 0;
    num++;

      FindBugs针对这样的类型的错误给出了对应的解释

    An increment to a volatile field isn’t atomic
    This code increments a volatile field. Increments of volatile fields aren’t atomic. If more than one thread is incrementing the field at the same time, increments could be lost.

      意即,对一个volatile字段进行自增操作。但这个字段不是原子类型的。假设多个线程同一时候对这个字段进行自增操作。可能会丢失数据。


      volatile是一个轻量级的synchronized的实现,针对volatile类型变量的操作都是线程安全的。volatile类型变量每次在读取的时候。都从主存中取。而不是从各个线程的“工作内存”。

    而非volatile型变量每次被读取的时候都是从线程的工作内存中读取主存中变量的一份拷贝。也就意味着假设非volatile型变量被某个线程改动,其他线程读取的可能是旧值。


    jvm内存模型图
    这里写图片描写叙述

      volatile类型变量每次在读取的时候,会越过线程的工作内存,直接从主存中读取。也就不会产生脏读。

    那为何FindBugs报这个错?

      根本原因在于++自增操作。Java的++操作对应汇编指令有三条
      1. 从主存读取变量值到cpu寄存器
      2. 寄存器里的值+1
      3. 寄存器的值写回主存

      假设N个线程同一时候运行到了第一步。那么终于变量会损失(N - 1)。第二步第三步仅仅有一个线程是运行成功。
      写个demo验证这个问题

    package com.alibaba.bop.tag.manager;
    
    import org.junit.Test;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * author   : lvsheng
     * date     : 2016/11/22 下午5:06
     */
    public class volatileTest {
    
        volatile int num = 0;
        int                coreSize = Runtime.getRuntime().availableProcessors();
        ThreadPoolExecutor exec     = new ThreadPoolExecutor(coreSize * 2, coreSize * 3, 0, TimeUnit.SECONDS,
                                                                    new LinkedBlockingQueue<Runnable>(500), new ThreadPoolExecutor.CallerRunsPolicy());
    
        @Test
        public void test() {
            for (int i = 0; i < Integer.MAX_VALUE; i++) {
                exec.execute(() -> num++);
            }
            System.out.println(Integer.MAX_VALUE);
            System.out.println(num);
            System.out.println("误差 : " + (Integer.MAX_VALUE - num));
        }
    }

      运行结果

    2147483647
    2121572795
    误差 :25910852

      自增操作整体上产生了1%的误差。FindBugs是个好工具。能找出自己知识体系范围外的bug。surprise!

  • 相关阅读:
    HTML5中表单的创建
    防冲撞协议原理实验报告
    yii2.0 Activeform表单部分组件使用方法 [ 2.0 版本 ]
    Yii正则验证
    Yii2用Gii自动生成Module+Model+CRUD
    yii2框架安装运行init.bat报错php.exe不是内部或外部命令
    YII2.0安装教程,数据库配置前后台 [ 2.0 版本 ]
    ignore_user_abort函数制定计划任务
    php 常用的系统函数
    php常用字符串处理函数
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8724004.html
Copyright © 2020-2023  润新知