volatile型变量自增操作的隐患

                       

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

volatile int num = 0;num++;
  
  
  • 1
  • 2

  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));    }}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

  执行结果

21474836472121572795误差 :25910852
  
  
  • 1
  • 2
  • 3

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

           
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值