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!
volatile型变量自增操作的隐患的更多相关文章
- volatile型变量语义讲解一 :对所有线程的可见性
volatile型变量语义讲解一 :对所有线程的可见性 一.volatile变量语义一的概念 当一个变量被定义成volatile之后,具备两个特性: 特性一:保证此变量对所有线程的可见性.这里的&qu ...
- Java虚拟机内存模型和volatile型变量
Java虚拟机内存模型 了解Java虚拟机的内存模型,有助于我们明白为什么会发生线程安全问题. 上面这幅图是<深入理解Java虚拟机-JVM高级特性与最佳实践>的书中截图. 线程共享的变量 ...
- Java线程角度的内存模型和volatile型变量
内存模型的目标是定义程序中各个变量的访问 规则,即在虚拟机中将变量(包括实例字段,静态字段和构成数组对象的元素,不包括局部变量与方法参数,因为后者是线程私有的)存储到内存和从内存中取出变量这样的底层细 ...
- synchronized同步块和volatile同步变量
Java语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量.这两种机制的提出都是为了实现代码线程的安全性.其中 Volatile 变量的同步性较差(但有时它更简单并且开销更低),而 ...
- Programming In Scala笔记-第五章、Scala中的变量类型和操作
这一章的一些基础性的东西,主要包括Scala中的基本变量类型,以及相关的一些操作符. 一.简单类型 下表中列出Scala语言中的基本类型,以及其字节长度,其中Byte, Short, Int, Lon ...
- seaborn 数据可视化(一)连续型变量可视化
一.综述 Seaborn其实是在matplotlib的基础上进行了更高级的API封装,从而使得作图更加容易,图像也更加美观,本文基于seaborn官方API还有自己的一些理解. 1.1.样式控制: ...
- Java-JUC(十三):现在有两个线程同时操作一个整数I,做自增操作,如何实现I的线程安全性?
问题分析:正如i在多线程中如果想实现i的多线程操作,必须i要使用volitle来保证其内存可见性,但是i++自增操作不具备原子性操作,因此需要对i++这段代码确保其原子性操作即可. 方案1: 使用Re ...
- bool型变量下标的时候javascript是不能允许的
jother编码是我最开始想写的内容,原因有两点:1.原理比较简单,不需要太多关于算法的知识.2.比较有趣,是在对javascript有了很深的理解之后催生的产物.如果你只需要知道jother编码和解 ...
- (转)C语言16进制输出字符型变量问题
最近在做一个C的嵌入式项目,发现在C语言中用printf()函数打印字符型变量时,如果想采用"%x"的格式将字符型变量值以十六进制形式打印出来,会出现一个小问题,如下: char ...
随机推荐
- Shell Script Basics
https://developer.apple.com/library/mac/documentation/OpenSource/Conceptual/ShellScripting/shell_scr ...
- HDU1385 (Floyd记录路径)
Minimum Transport Cost Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/O ...
- camera驱动框架分析(中)
camera host的驱动 下面开始分析camera host吧,如果仅仅是想知道camera sensor驱动怎么写,而不想知道内部具体怎么个调用流程,怎么个架构设计,那可以跳过该部分,直接去看i ...
- 利用git把本地项目传到github+将github中已有项目从本地上传更新
利用git把本地项目传到github中 1.打开git bash命令行,进入到要上传的项目中,比如Spring项目,在此目录下执行git init 的命令,会发下在当前目录中多了一个.git的文件夹( ...
- 安装并启用rabbitmq服务器
1.确保Erlang已经安装 2.安装rabbitmq服务器 [root@bogon yum.repos.d]# rpm --import https://dl.bintray.com/rabbitm ...
- 曹政:CTO这点事
几乎整个互联网行业都缺 CTO,特别是一些草根背景的创业者,这个问题更加显著.从我自己的感受,身边各种朋友委托我找 CTO 的需求,嗯,算下来超过两位数了,光最近一个月就有 3 个,而且这三家都是刚拿 ...
- [BZOJ1491][NOI2007]社交网络 floyd
1491: [NOI2007]社交网络 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 2196 Solved: 1170[Submit][Status ...
- python实用技巧 : Filtering os.walk(转)
''' Created on Mar 7, 2010 @author: Diego 需求: 得到某个目录下, 符合过滤条件的文件夹/文件.实现: 将os.walk再次包装. TODO: 不知道本程序的 ...
- 洛谷 P1570 KC喝咖啡【二分答案/最大化平均值模板】
题目描述 话说KC和SH在福州的时候常常跑去85°C喝咖啡或者其他的一些什么东西. 这天,KC想要喝一杯咖啡,服务员告诉他,现在有n种调料,这杯咖啡只可以加入其中的m种(当然KC一定会加入m种,不会加 ...
- codeforces Round #441 A Trip For Meal【思路/模拟】
A. Trip For Meal time limit per test 1 second memory limit per test 512 megabytes input standard inp ...