在synchronized块中,i=2与i++执行结果差异何在?

2026-04-28 15:082阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1213个文字,预计阅读时间需要5分钟。

在synchronized块中,i=2与i++执行结果差异何在?

在`synchronized`块下,执行`i +=2`和`i++`的结果不一致。原因分析如下:

1. 字节码分析: - `i +=2`对应的字节码是`iadd`,直接将`i`的值加2。 - `i++`对应的字节码是`iinc`,先将`i`的值加1,然后返回原始值。

2. 原因: - 当多个线程同时执行`synchronized`块时,由于`i`的内存地址是相同的,`iadd`和`iinc`操作会按照字节码的执行顺序进行。 - 由于`iinc`操作会先读取`i`的原始值,然后加1,再返回原始值,所以当其他线程执行`i++`时,会读取到上一个线程修改后的值,导致结果不一致。

3. 解决方案: - 使用`i +=2`代替`i++`,确保每次操作都是对`i`的值进行累加。 - 使用`volatile`关键字修饰`i`变量,确保每次读取`i`的值都是最新的。

4. 相关链接: - 问题链接:[https://q.cnblogs.com/q/140032/](https://q.cnblogs.com/q/140032/)

synchronized,synchronized下的 i+=2 和 i++ i++执行结果居然不一样,字节码分析 起因

逛时发现了一段有意思的问题:

问题链接:q.cnblogs.com/q/140032/

这段代码是这样的:

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class AutomicityTest implements Runnable { private int i = 0; public int getValue() { return i; } /** * 同步方法,加2 */ public synchronized void evenIncrement() { i += 2; // i++; // i++; } @Override public void run() { while (true) { evenIncrement(); } } public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); // AutomicityTest线程 AutomicityTest automicityTest = new AutomicityTest(); exec.execute(automicityTest); // main线程 while (true) { int value = automicityTest.getValue(); if (value % 2 != 0) { System.out.println(value); System.exit(0); } } } }

代码很简单(一开始我没看清楚问题,还建议楼主去学习下Java语法,尴尬的一批~~~)

简单说明下代码逻辑

一、AutomicityTest类实现了Runnable接口,并实现 run() 方法,run() 方法中有一个 while(true) 循环,循环体中调用了一个 synchronized 修饰的方法 evenIncrement();

二、AutomicityTest类中有一个 int i 成员变量;

三、在 main() 方法中使用线程池执行线程(直接 new Thread().start() 是一样的),然后 while(true) 循环体中不停打印成员变量 i 的值,如果是奇数就退出虚拟机。

在synchronized块中,i=2与i++执行结果差异何在?

大家觉得这段代码会输出什么呢?

没错就是死循环!!!

有意思的地方来了,如果我把同步方法 evenIncrement() 改为下面这样:

public synchronized void evenIncrement() { // i += 2; i++; i++; }

执行结果是退出了虚拟机!!!

是不是很纳闷儿,要是不纳闷儿,就不用往下看了 >_<

一起来分析一下

1.首先从方法入口开始,main() 方法当中创建了一个AutomicityTest线程 和 本身 main() 所在的main线程,所以AutomicityTest线程是一个写线程,main线程是一个读线程,既然有读有写,又是多个线程,就涉及到工作内存和主存模型,在这里我就不赘述了。

2.写线程是有synchronized修饰的,但是读线程并没有,这就导致了读写不一致,解决方法就是给 getValue() 加上synchronized,此时执行结果就正常了

3.但是问题还没完,为什么 i += 2 死循环,而 i++ 却退出了虚拟机呢?

字节码层面分析

public synchronized void evenIncrement() { i += 2; } // 对应的字节码 public synchronized void evenIncrement(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=3, locals=1, args_size=1 0: aload_0 1: dup 2: getfield #2 // Field i:I 5: iconst_2 6: iadd 7: putfield #2 // Field i:I 10: return

public synchronized void evenIncrement() { i++; i++; } // 对应的字节码 public synchronized void evenIncrement(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=3, locals=1, args_size=1 0: aload_0 1: dup 2: getfield #2 // Field i:I 5: iconst_1 6: iadd 7: putfield #2 // Field i:I 10: aload_0 11: dup 12: getfield #2 // Field i:I 15: iconst_1 16: iadd 17: putfield #2 // Field i:I 20: return i+=2;

字节码指令只有一段putfield,没有执行是偶数,执行了也是偶数,所以会死循环。

i++; i++;

字节码指令有两段putfield,由于之前getValue()没有加synchronized,那么在执行getValue()的时候,putfield可能没有执行,可能执行了一次,也可能执行了两次,没有执行是偶数,执行一次是奇数,执行两次是偶数;又因为AutomicityTest线程 run() 是 while (true) {} 的,所以它总能执行到奇数,退出虚拟机。

最后

到此就分析完了,所以该问题的关键就是写操作是原子的,但是读操作不是,导致读出来的数据不是最终的。

本文共计1213个文字,预计阅读时间需要5分钟。

在synchronized块中,i=2与i++执行结果差异何在?

在`synchronized`块下,执行`i +=2`和`i++`的结果不一致。原因分析如下:

1. 字节码分析: - `i +=2`对应的字节码是`iadd`,直接将`i`的值加2。 - `i++`对应的字节码是`iinc`,先将`i`的值加1,然后返回原始值。

2. 原因: - 当多个线程同时执行`synchronized`块时,由于`i`的内存地址是相同的,`iadd`和`iinc`操作会按照字节码的执行顺序进行。 - 由于`iinc`操作会先读取`i`的原始值,然后加1,再返回原始值,所以当其他线程执行`i++`时,会读取到上一个线程修改后的值,导致结果不一致。

3. 解决方案: - 使用`i +=2`代替`i++`,确保每次操作都是对`i`的值进行累加。 - 使用`volatile`关键字修饰`i`变量,确保每次读取`i`的值都是最新的。

4. 相关链接: - 问题链接:[https://q.cnblogs.com/q/140032/](https://q.cnblogs.com/q/140032/)

synchronized,synchronized下的 i+=2 和 i++ i++执行结果居然不一样,字节码分析 起因

逛时发现了一段有意思的问题:

问题链接:q.cnblogs.com/q/140032/

这段代码是这样的:

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class AutomicityTest implements Runnable { private int i = 0; public int getValue() { return i; } /** * 同步方法,加2 */ public synchronized void evenIncrement() { i += 2; // i++; // i++; } @Override public void run() { while (true) { evenIncrement(); } } public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); // AutomicityTest线程 AutomicityTest automicityTest = new AutomicityTest(); exec.execute(automicityTest); // main线程 while (true) { int value = automicityTest.getValue(); if (value % 2 != 0) { System.out.println(value); System.exit(0); } } } }

代码很简单(一开始我没看清楚问题,还建议楼主去学习下Java语法,尴尬的一批~~~)

简单说明下代码逻辑

一、AutomicityTest类实现了Runnable接口,并实现 run() 方法,run() 方法中有一个 while(true) 循环,循环体中调用了一个 synchronized 修饰的方法 evenIncrement();

二、AutomicityTest类中有一个 int i 成员变量;

三、在 main() 方法中使用线程池执行线程(直接 new Thread().start() 是一样的),然后 while(true) 循环体中不停打印成员变量 i 的值,如果是奇数就退出虚拟机。

在synchronized块中,i=2与i++执行结果差异何在?

大家觉得这段代码会输出什么呢?

没错就是死循环!!!

有意思的地方来了,如果我把同步方法 evenIncrement() 改为下面这样:

public synchronized void evenIncrement() { // i += 2; i++; i++; }

执行结果是退出了虚拟机!!!

是不是很纳闷儿,要是不纳闷儿,就不用往下看了 >_<

一起来分析一下

1.首先从方法入口开始,main() 方法当中创建了一个AutomicityTest线程 和 本身 main() 所在的main线程,所以AutomicityTest线程是一个写线程,main线程是一个读线程,既然有读有写,又是多个线程,就涉及到工作内存和主存模型,在这里我就不赘述了。

2.写线程是有synchronized修饰的,但是读线程并没有,这就导致了读写不一致,解决方法就是给 getValue() 加上synchronized,此时执行结果就正常了

3.但是问题还没完,为什么 i += 2 死循环,而 i++ 却退出了虚拟机呢?

字节码层面分析

public synchronized void evenIncrement() { i += 2; } // 对应的字节码 public synchronized void evenIncrement(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=3, locals=1, args_size=1 0: aload_0 1: dup 2: getfield #2 // Field i:I 5: iconst_2 6: iadd 7: putfield #2 // Field i:I 10: return

public synchronized void evenIncrement() { i++; i++; } // 对应的字节码 public synchronized void evenIncrement(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=3, locals=1, args_size=1 0: aload_0 1: dup 2: getfield #2 // Field i:I 5: iconst_1 6: iadd 7: putfield #2 // Field i:I 10: aload_0 11: dup 12: getfield #2 // Field i:I 15: iconst_1 16: iadd 17: putfield #2 // Field i:I 20: return i+=2;

字节码指令只有一段putfield,没有执行是偶数,执行了也是偶数,所以会死循环。

i++; i++;

字节码指令有两段putfield,由于之前getValue()没有加synchronized,那么在执行getValue()的时候,putfield可能没有执行,可能执行了一次,也可能执行了两次,没有执行是偶数,执行一次是奇数,执行两次是偶数;又因为AutomicityTest线程 run() 是 while (true) {} 的,所以它总能执行到奇数,退出虚拟机。

最后

到此就分析完了,所以该问题的关键就是写操作是原子的,但是读操作不是,导致读出来的数据不是最终的。