引用赋值的原子性问题

  • 一、问题及来源
  • 二、分析
    • 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."<init>":()V
       4return
  public static void main(java.lang.String[]);
    Code:
       0new           #2                  // class K, new K() start
       3: dup
       4: invokespecial #3                  // Method K."<init>":()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."<init>":()V
       4return
  public static void main(java.lang.String[]);
 
    Code:
       0new           #2                  // class K
       3: dup
       4: invokespecial #3                  // Method K."<init>":()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."<init>":()V
       4return
  public static void main(java.lang.String[]);
    Code:
       0new           #2                  // class K
       3: dup
       4: invokespecial #3                  // Method K."<init>":()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`是原子的。

原文地址:https://www.cnblogs.com/chenmingming0225/p/12922289.html