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 ...
随机推荐
- 分享三个USB抓包软件---Bus Hound,USBlyzer 和-USBTrace【转】
转自:http://bbs.armfly.com/read.php?tid=15377 Bus Hound官方下载地址:http://perisoft.net/bushound/ Bus Hound ...
- python基础===map和zip的用法
>>> list1=[1,45,232,45,666,64] >>> list2=["ss","kein","to ...
- appium的使用
这套教程年久失修,问题的人也比较多,于是,我重新整理了一套appium入门教程. appium新手入门(1)—— appium介绍 appium新手入门(2)—— 安装 Android SDK app ...
- java小工具——timer
定时器Timer 用途:做定时器用的. public static void main(String[] args) { Timer timer = new Timer(); timer.schedu ...
- 使用DRF视图集时自定义action方法
在我们用DRF视图集完成了查找全部部门,创建一个新的部门,查找一个部门,修改一个部门,删除一个部门的功能后,views.py的代码是这样子的: class DepartmentViewSet(Mode ...
- 分析函数调用堆栈的原理和Delphi实现
来自:http://blog.163.com/liuguang_123/blog/static/816701920105262543890/ ----------------------------- ...
- Appium+python自动化29-toast消息【转载】
本篇转自博客:上海-悠悠 前言 appium1.5以后的版本才支持toast定位,并且 'automationName'得设置为'Uiautomator2',才能捕获到. 一. Supported P ...
- HDU 1465.装错信封-递推
不容易系列之一 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Sub ...
- AMQ学习笔记 - 04. 消息选择器
概述 消息选择器使用类似于SQL语法,为Consumer指定基于Message属性的筛选条件. 消息选择器 发送的时候,给消息添加一些属性:在接收的时候,根据属性进行过滤. API javax.jms ...
- POJ1128 (TopSort)(递归)(回溯)
Frame Stacking Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 5220 Accepted: 1809 De ...