这是美团一面面试官的一个问题,后来发现这是一道面试常见题,怪自己没有准备充分:i++;在多线程环境下是否存在问题?当时回答存在,接着问,那怎么解决?。。。好吧,我说加锁或者synchronized同步方法。接着问,那有没有更好的方法?

  经过一番百度、谷歌,还可以用AtomicInteger这个类,这个类提供了自增、自减等方法(如i++或++i都可以实现),这些方法都是线程安全的。

一、补充概念


1.什么是线程安全性?

  《Java Concurrency in Practice》中有提到:当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的。

2.Java中的“同步”

  Java中的主要同步机制是关键字“synchronized”,它提供了一种独占的加锁方式,但“同步”这个术语还包括volatile类型的变量,显式锁(Explicit Lock)以及原子变量。

2.原子性

  原子是世界上的最小单位,具有不可分割性。比如 a=0;(a非long和double类型)这个操作是不可分割的,那么我们说这个操作时原子操作。再比如:a++;这个操作实际是a = a + 1;是可分割的,所以他不是一个原子操作。非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。一个操作是原子操作,那么我们称它具有原子性。java的concurrent包下提供了一些原子类,我们可以通过阅读API来了解这些原子类的用法。比如:AtomicInteger、AtomicLong、AtomicReference等。

二、实例源码


 public class IncrementTestDemo {

     public static int count = 0;
public static Counter counter = new Counter();
public static AtomicInteger atomicInteger = new AtomicInteger(0);
volatile public static int countVolatile = 0; public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread() {
public void run() {
for (int j = 0; j < 1000; j++) {
count++;
counter.increment();
atomicInteger.getAndIncrement();
countVolatile++;
}
}
}.start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("static count: " + count);
System.out.println("Counter: " + counter.getValue());
System.out.println("AtomicInteger: " + atomicInteger.intValue());
System.out.println("countVolatile: " + countVolatile);
} } class Counter {
private int value; public synchronized int getValue() {
return value;
} public synchronized int increment() {
return ++value;
} public synchronized int decrement() {
return --value;
}
}

  输出结果:

static count: 9952
Counter: 10000
AtomicInteger: 10000
countVolatile: 9979

  第一行与最后一行,每次运行将得到不同的结果,但是中间两行的结果相同。

  通过上面的例子说明,要解决自增操作在多线程环境下线程不安全的问题,可以选择使用Java提供的原子类,或者使用synchronized同步方法。

  而通过Volatile关键字,并不能解决非原子操作的线程安全性。Volatile详解

三、Java中的自增原理


  虽然递增操作++i是一种紧凑的语法,使其看上去只是一个操作,但这个操作并非原子的,因而它并不会作为一个不可分割的操作来执行。实际上,它包含了三个独立的操作:读取count的值,将值加1,然后将计算结果写入count。这是一个“读取 - 修改 - 写入”的操作序列,并且其结果状态依赖于之前的状态。

  下面写一个简单的类,用jdk中的工具javap来反编译Java字节码文件。

/**
* @author zhengbinMac
*/
public class TestDemo {
public static int count; public void code() {
count++;
}
}
localhost:Increment zhengbinMac$ javap -c TestDemo
警告: 二进制文件TestDemo包含Increment.TestDemo
Compiled from "TestDemo.java"
public class Increment.TestDemo {
public static int count; public Increment.TestDemo();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return public void code();
Code:
0: getstatic #2 // Field count:I
3: iconst_1
4: iadd
5: putstatic #2 // Field count:I
8: return
}

  如上字节码,我们发现自增操作包括取数(getstatic  #2)、加一(iconst_1和iadd)、保存(putstatic  #2),并不是我们认为的一条机器指令搞定的。

参考博文:

关于Java自增操作的原子性分析

JAVA中如何保证线程安全以及主键自增有序

Java - 并发 atomic, synchronization and volatile

java并发之原子性与可见性(一)

Java自增原子性问题(测试Volatile、AtomicInteger)的更多相关文章

  1. Java多线程学习(三)volatile关键字

    转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79680693 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...

  2. 【Java并发编程】6、volatile关键字解析&内存模型&并发编程中三概念

    volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以 ...

  3. Java并发编程(六)volatile关键字解析

    由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识. 一.内存模型的相关概念 Java内存模型规定所有的变量都是存在 ...

  4. JAVA多线程基础学习三:volatile关键字

    Java的volatile关键字在JDK源码中经常出现,但是对它的认识只是停留在共享变量上,今天来谈谈volatile关键字. volatile,从字面上说是易变的.不稳定的,事实上,也确实如此,这个 ...

  5. Java并发(3)- 聊聊Volatile

    引言 谈到volatile关键字,大多数开发者都有一定了解,可以说是开发者非常熟悉,深入之后又非常陌生的一个关键字.相当于轻量的synchronized,也叫轻量级锁,与synchronized相比性 ...

  6. Java并发编程(三)volatile域

    相关文章 Java并发编程(一)线程定义.状态和属性 Java并发编程(二)同步 Android多线程(一)线程池 Android多线程(二)AsyncTask源代码分析 前言 有时仅仅为了读写一个或 ...

  7. Java之先行发生原则与volatile关键字详解

    volatile关键字可以说是Java虚拟机提供的最轻量级的同步机制,但是它并不容易完全被正确.完整地理解,以至于许多程序员都习惯不去使用它,遇到需要处理多线程数据竞争问题的时候一律使用synchro ...

  8. Java高并发--原子性可见性有序性

    Java高并发--原子性可见性有序性 主要是学习慕课网实战视频<Java并发编程入门与高并发面试>的笔记 原子性:指一个操作不可中断,一个线程一旦开始,直到执行完成都不会被其他线程干扰.换 ...

  9. Java Thread系列(六)volatile

    Java Thread系列(六)volatile volatile 关键字具备可见性,不具备原子性.主要作用是使变量在多个线程间可见.但不具备原子性(同步性),可以算一个轻量级的 synchroniz ...

随机推荐

  1. WCF分布式开发步步为赢(3)WCF服务元数据交换、配置及编程开发

    今天我们继续WCF分布式开发步步为赢(3)WCF服务元数据交换.配置及编程开发的学习.经过前面两节的学习,我们了解WCF分布式开发的相关的基本的概念和自定义宿主托管服务的完整的开发和配置过程.今天我们 ...

  2. 传说中的WCF(6):数据协定(b)

    我们继续,上一回我们了解了数据协定的一部分内容,今天我们接着来做实验.好的,实验之前先说一句:实验有风险,写代码须谨慎. 实验开始!现在,我们定义两个带数据协定的类——Student和AddrInfo ...

  3. JAVA IO 类库详解

    JAVA IO类库详解 一.InputStream类 1.表示字节输入流的所有类的超类,是一个抽象类. 2.类的方法 方法 参数 功能详述 InputStream 构造方法 available 如果用 ...

  4. hdu 4462(状态压缩)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4462 思路:由于数据不大,可以直接将所有的状态表示出来,然后枚举,判断能否将方格全部覆盖. http: ...

  5. spring_150801_autowired_qualifier

    新建java project工程,建src.conf.test源码文件夹,导入相关包,需要spring的相关jar包和common-logging相关jar包 接口Service: package c ...

  6. Linux系统调用--getrlimit()与setrlimit()函数详解

    http://www.cnblogs.com/niocai/archive/2012/04/01/2428128.html 功能描述:获取或设定资源使用限制.每种资源都有相关的软硬限制,软限制是内核强 ...

  7. 性能标准:Apdex介绍

    目前的应用程序性能测试工具有多方面的局限.每种工具都有其自己对性能的定义,产生太多混乱或矛盾的数值,并缺少简洁的结论.IT经理们无法深入了解性能,而这是软件与用户对关键应用的体验有关的事. 听云作为新 ...

  8. RabbitMQ安装与搭建

    1.下载Erlang,最新版本是erl8.1 ,地址:http://erlang.org/download/otp_win64_19.1.exe 网上很多资料说安装后要配置环境变量,不过我安装最新的版 ...

  9. QTreeView使用点点滴滴

    QTreeView比较复杂,在这里记下所有用到的操作: ------------------------------------------------------------------------ ...

  10. [iOS]提交App报错ERROR ITMS -90207

    前几天上传项目N多次,都跳出这个问题 甚是头痛,于是乎各种搜索 1. 第三方的info.plist里面Executable file这个要删除(自己的不能删哦) 2.检查一下用来做跳转到第三方应用的设 ...