关于Java自增操作的原子性
来源:互联网 发布:英属哥伦比亚大学 知乎 编辑:程序博客网 时间:2024/05/13 19:55
最近在工作中和一个同事因为自增是不是原子性操作争论的面红耳赤,那Java的自增操作到底是不是原子性操作呢,答案是否的,即Java的自增操作不是原子性操作。
1.首先我们先看看Bruce Eckel是怎么说的:
In the JVM an increment is not atomic and involves both a read and a write. (via the latest Java Performance Tuning Newsletter)
意思很简单,就是说在jvm中自增不是原子性操作,它包含一个读操作和一个写操作。
2.以上可能还不能让你信服,要想让人心服口服,就必须用代码说话。正如FaceBook的文化一样:代码赢得争论。那我们就看一段代码:
以下的代码是用100个线程同时执行自增操作,每个线程自增100次,如果自增操作是原子性操作的话,那么执行完amount的值为10,000。运行代码之后,你会发现amount的值小于10,000,这就说明自增操作不是原子性的
- /**
- *
- * @author renrun.wu
- */
- public class MultiThread implements Runnable {
- private int count;
- private int amount = 1;
- public MultiThread() {
- count = 100;
- }
- public MultiThread(int count) {
- this.count = count;
- }
- @Override
- public void run() {
- for (int i = 0; i < count; i++) {
- amount++;
- }
- }
- public static void main(String[] args) {
- ExecutorService executorService = Executors.newCachedThreadPool();
- MultiThread multiThread =new MultiThread();
- for (int i = 0; i < 100; i++) {
- executorService.execute(multiThread);
- }
- executorService.shutdown();
- try {
- Thread.sleep(60000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(multiThread.amount);
- }
- }
3.如果以上还不能让你信服的话,也没关系。我们就把自增操作反编译出来,看看java字节码是怎么操作的
以下是一个简单的自增操作代码
- public class Increment {
- private int id = 0;
- public void getNext(){
- id++;
- }
- }
我们看看反编译之后的Java字节码,主要关注getNext()方法内部的Java字节码。
- public class Increment extends java.lang.Object{
- public Increment();
- Code:
- : aload_0
- : invokespecial #1; //Method java/lang/Object."<init>":()V
- : aload_0
- : iconst_0
- : putfield #2; //Field id:I
- : return
- public void getNext();
- Code:
- : aload_0 //加载局部变量表index为0的变量,在这里是this
- : dup //将当前栈顶的对象引用复制一份
- : getfield #2; //Field id:I,获取id的值,并将其值压入栈顶
- : iconst_1 //将int型的值1压入栈顶
- : iadd //将栈顶两个int类型的元素相加,并将其值压入栈顶
- : putfield #2; //Field id:I,将栈顶的值赋值给id
- : return
- }
很明显,我们能够看到在getNext()方法内部,对于类变量id有一个先取值后加一再赋值的过程。因此,我们可以很肯定的说Java中的自增操作不是原子性的。
4.也许你会问,那局部变量的自增操作是否是原子性的。好,我们在看看一下代码:
- public class Increment {
- public void getNext(){
- int id = 0;
- id++;
- }
- }
我们再看看反编译之后的Java字节码,主要还是关注getNext()方法内部的Java字节码。
- public class Increment extends java.lang.Object{
- public Increment();
- Code:
- : aload_0
- : invokespecial #1; //Method java/lang/Object."<init>":()V
- : return
- public void getNext();
- Code:
- : iconst_0
- : istore_1
- : iinc 1, 1
- : return
- }
与全局变量的自增操作相比,很明显局部变量的自增操作少了getfield与putfield操作。而且对于局部变量来说,它无论如何都不会涉及到多线程的操作,因此局部变量的自增操作是否是原子操作也就显得不那么重要了。
- 关于Java自增操作的原子性
- 关于Java自增操作的原子性
- 关于Java自增操作的原子性
- 关于Java自增操作的原子性
- java中的自增过程不是原子性操作
- C++自增操作与原子性
- Java数据类型操作的原子性
- Java数据类型操作的原子性
- Django原子性更新数据库——数据库表中属性的自增/自减操作
- Java自增原子性问题(测试Volatile、AtomicInteger)
- Java自增原子性问题(测试Volatile、AtomicInteger)
- java中的原子性操作
- Java中关于原子性
- 关于原子操作的一些总结
- 关于atomic_t 原子变量的操作
- 关于文件I/o的原子操作
- Java原子变量&原子操作
- 【java并发】原子性操作类的使用
- 关于Java自增操作的原子性
- linux打包压缩的时候如何剔除某些不想打包的进来的文件
- onClick事件中含有异步执行的方法,如何让异步方法执行完毕后onClick事件方可重新响应点击事件
- C#与C/C++的交互
- win7无法创建还原点 卷影复制服务组件遇到了意外错误0x80042302
- 关于Java自增操作的原子性
- Qt命名规则总结
- 安装eclipse
- 简单的属性文件读写工具类
- 图的深度优先遍历和广度优先遍历算法
- PHP 生成一定数量的不重复随机数
- ubuntu12.4以root账户登录
- 如何利用nslookup命令查询mx记录?以及邮件相关记录
- 项目中的多重登录检测处理