引用赋值的原子性问题


  • 一、问题及来源
  • 二、分析
    • 2.1 官方引证
    • 2.2 过程分析

一、问题及来源

对原始类型不包括(long,double)变量进行赋值是原子操作,但是没有找到资料对引用类型的变量的赋值操作的原子性进行说明.例如 Object var = otherObjectValue; 这是原子操作吗?

最近在看并发编程,此问题由int[] arr = new int[0]是不是原子操作而引出。

二、分析

2.1 官方引证

向 reference 赋值是原子的(obj = otherObjectValue):

https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7

2.2 过程分析

我认为是原子操作 ,声明立刻赋值和赋值是等价的(Object var = objValue; 和 var = objValue 等价):

K k = source;

K k;
k = source;

编译出的字节码是一样的;(使用 javap -c):

class K {       private int c;     public int getC() {         return c;     }     public void setC(int c) {         this.c = c;     } }
class T {     public static void main(String[] args) {         K source = new K();         K k = source;         k.getC();     } }
T的字节码 折叠原码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Compiled from "T.java"   class T {   T();     Code:        0: aload_0        1: invokespecial #1                  // Method java/lang/Object."":()V        4return   public static void main(java.lang.String[]);     Code:        0new           #2                  // class K, new K() start        3: dup        4: invokespecial #3                  // Method K."":()V        7: astore_1                          // source = new K()        8: aload_1                           // new K() end, prepare `source`        9: astore_2                          // k = source;       10: aload_2                           // prepare `k`       11: invokevirtual #4                  // Method K.getC:()I       14: pop       15return }
class T2 {     public static void main(String[] args) {         K source = new K();         K k;         k = source;         k.getC();     } }
T1的字节码 折叠原码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Compiled from "T2.java" class T2 {   T2();     Code:        0: aload_0        1: invokespecial #1                  // Method java/lang/Object."":()V        4return   public static void main(java.lang.String[]);       Code:        0new           #2                  // class K        3: dup        4: invokespecial #3                  // Method K."":()V        7: astore_1        8: aload_1        9: astore_2       10: aload_2       11: invokevirtual #4                  // Method K.getC:()I       14: pop       15return }
class T2 {     public static void main(String[] args) {         K source = new K();         K k;         k = source;         k.getC();     } }
T2的字节码 折叠原码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Compiled from "T3.java" class T3 {   static K k;   T3();     Code:        0: aload_0        1: invokespecial #1                  // Method java/lang/Object."":()V        4return   public static void main(java.lang.String[]);     Code:        0new           #2                  // class K        3: dup        4: invokespecial #3                  // Method K."":()V        7: astore_1        8: aload_1        9: putstatic     #4                  // Field k:LK;       12: getstatic     #4                  // Field k:LK;       15: invokevirtual #5                  // Method K.getC:()I       18: pop       19return }

由 T1 和 T2 ,声明一个 reference 并立刻赋值( Object obj = otherObjectValue )是原子的。

在字节码里是 2 句指令 `aload`和`astore`,但是这两句根据上面提到的 JLS ,原子性是有保证的:

1. k = source 等价于
    ```
   8: aload_1 // source 压栈
   9: astore_2 // source 出栈给 k
   ```
2. k = source 是 reference 赋值
3. reference 赋值根据 JLS ,是原子的
    => 用于 reference 赋值的`aload + astore`是原子的。