Eclipse的$ SWITCH_TABLE $线程安全吗?

时间:2019-10-22 15:47:48

标签: java eclipse java-memory-model

当Eclipse编译在switch语句中使用其值的枚举时,它会出现,它将以下内容添加到枚举类文件中:

  • 合成字段:private static int[] $SWITCH_TABLE$mypackage$MyEnum;
  • 合成方法:static int[] $SWITCH_TABLE$mypackage$MyEnum()
    用各自的枚举序数值懒惰地初始化$SWITCH_TABLE$mypackage$MyEnum字段。

在每个枚举值发生切换的地方,它都会调用$SWITCH_TABLE$mypackage$MyEnum()[e.ordinal()]

源代码:

package mypackage;

public enum MyEnum {
    FIRST;

    public int get() {
        switch (this) {
            case FIRST:
                return 2;
            default:
                return -1;
        }
    }
}

反编译代码:

package mypackage;

public enum MyEnum {
   FIRST;

   // $FF: synthetic field
   private static int[] $SWITCH_TABLE$mypackage$MyEnum;

   public int get() {
      switch($SWITCH_TABLE$mypackage$MyEnum()[this.ordinal()]) {
      case 1:
         return 2;
      default:
         return -1;
      }
   }

   // $FF: synthetic method
   static int[] $SWITCH_TABLE$mypackage$MyEnum() {
      int[] var10000 = $SWITCH_TABLE$mypackage$MyEnum;
      if (var10000 != null) {
         return var10000;
      } else {
         int[] var0 = new int[values().length];

         try {
            var0[FIRST.ordinal()] = 1;
         } catch (NoSuchFieldError var1) {
         }

         $SWITCH_TABLE$mypackage$MyEnum = var0;
         return var0;
      }
   }
}

但是,正如您在此处看到的那样,字段$SWITCH_TABLE$mypackage$MyEnum都不是volatile也不是$SWITCH_TABLE$mypackage$MyEnum()进行任何同步。

现在的问题是:这实际上是线程安全的吗?如果我正确理解Java内存模型,则不能保证如果一个线程(通过对get()的调用)初始化了切换表,则另一个线程看到了最新的切换表值。相反,它可能会看到没有正确元素的陈旧值。

后续问题:由于没有 happens-before 关系,是否还可以允许JVM在元素更改之前重新排序对$SWITCH_TABLE$的分配?如果可能的话,可能存在竞争情况,其中一个线程看到数组,而另一个线程仍在修改元素。

1 个答案:

答案 0 :(得分:0)

我在Windows 7 / OpenJDK 13上使用jcstress运行了一些测试。本地变量很重要!

此代码应模仿发布的代码,并多次运行3个线程:

// concurrency stress test
@JCStressTest

// expected outcome (III_Result::toString)
@Outcome(id = "1, 1, 1", expect = Expect.ACCEPTABLE, desc = "Correctly initialized.")
@Outcome(id = ".+, .+, .+", expect = Expect.FORBIDDEN, desc = "Some Thread did see a not initialized array")

// test data
@State
public class EnumInitOK {

    int[] array;

    int[] switchTable() {
        int[] v1 = array;
        if (v1 != null) {
            return v1;
        } else {
            int[] v2 = new int[1];
            v2[0] = 1;
            array = v2;
            return v2;
        }
    }

    // each called only by one particular Thread, once per State instance
    @Actor public void test1(III_Result r) {
        r.r1 = switchTable()[0];
    }

    @Actor public void test2(III_Result r) {
        r.r2 = switchTable()[0];
    }

    @Actor public void test3(III_Result r) {
        r.r3 = switchTable()[0];
    }
}

使用不同设置进行所有运行的结果,而不是一个初始化错误(将近400M测试):

Java Concurrency Stress Tests
---------------------------------------------------------------------------------
Rev: null, built by cfh with 12.0.2 at null

Probing what VM modes are available:
 (failures are non-fatal, but may miss some interesting cases)

----- [OK] [-Xint]
----- [OK] [-XX:TieredStopAtLevel=1]
----- [OK] []
----- [OK] [-XX:+UnlockDiagnosticVMOptions, -XX:+StressLCM, -XX:+StressGCM]
----- [OK] [-XX:-TieredCompilation]
----- [OK] [-XX:-TieredCompilation, -XX:+UnlockDiagnosticVMOptions, -XX:+StressLCM, -XX:+StressGCM]

Initializing and probing the target VM: 
 (all failures are non-fatal, but may affect testing accuracy)

----- [OK] Unlocking diagnostic VM options
Burning up to figure out the exact CPU count....... done!

----- [OK] Trimming down the default VM heap size to 1/4-th of max RAM
----- [OK] Trimming down the number of compiler threads
----- [OK] Trimming down the number of parallel GC threads
----- [OK] Trimming down the number of concurrent GC threads
----- [OK] Trimming down the number of G1 concurrent refinement GC threads
----- [OK] Testing @Contended works on all results and infra objects
----- [OK] Unlocking Whitebox API for online de-optimization
----- [OK] Testing allocation profiling
----- [OK] Trying Thread.onSpinWait

  Hardware threads in use/available: 4/4, using Thread.onSpinWait()
  Test preset mode: "default"
  Writing the test results to "jcstress-results-2019-10-22-23-32-40.bin.gz"
  Parsing results to "results/"
  Running each test matching "cfh.jcstress.EnumInitOK" for 1 forks, 5 iterations, 1000 ms each
  Each JVM would execute at most 5 tests in the row.
  Solo stride size will be autobalanced within [10, 10000] elements, but taking no more than 100 Mb.

... [output deleted] ...

RUN COMPLETE.

RUN RESULTS:
------------------------------------------------------------------------------------------------------------------------

*** INTERESTING tests
  Some interesting behaviors observed. This is for the plain curiosity.

  0 matching test results. 

*** FAILED tests
  Strong asserts were violated. Correct implementations should have no assert failures here.

  0 matching test results. 

*** ERROR tests
  Tests break for some reason, other than failing the assert. Correct implementations should have none.

  0 matching test results. 

*** All remaining tests
  Tests that do not fall into any of the previous categories.

  6 matching test results. 
      [OK] cfh.jcstress.EnumInitOK
    (JVM args: [-XX:+UnlockDiagnosticVMOptions, -XX:+StressLCM, -XX:+StressGCM])
  Observed state   Occurrences   Expectation  Interpretation                                              
      .+, .+, .+             0     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    76.194.931    ACCEPTABLE  Correctly initialized.                                      

      [OK] cfh.jcstress.EnumInitOK
    (JVM args: [-XX:-TieredCompilation, -XX:+UnlockDiagnosticVMOptions, -XX:+StressLCM, -XX:+StressGCM])
  Observed state   Occurrences   Expectation  Interpretation                                              
      .+, .+, .+             0     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    76.756.011    ACCEPTABLE  Correctly initialized.                                      

      [OK] cfh.jcstress.EnumInitOK
    (JVM args: [-XX:-TieredCompilation])
  Observed state   Occurrences   Expectation  Interpretation                                              
      .+, .+, .+             0     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    78.453.381    ACCEPTABLE  Correctly initialized.                                      

      [OK] cfh.jcstress.EnumInitOK
    (JVM args: [-XX:TieredStopAtLevel=1])
  Observed state   Occurrences   Expectation  Interpretation                                              
      .+, .+, .+             0     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    73.608.821    ACCEPTABLE  Correctly initialized.                                      

      [OK] cfh.jcstress.EnumInitOK
    (JVM args: [-Xint])
  Observed state   Occurrences   Expectation  Interpretation                                              
      .+, .+, .+             0     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1     8.740.641    ACCEPTABLE  Correctly initialized.                                      

      [OK] cfh.jcstress.EnumInitOK
    (JVM args: [])
  Observed state   Occurrences   Expectation  Interpretation                                              
      .+, .+, .+             0     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    86.069.111    ACCEPTABLE  Correctly initialized.                                      


------------------------------------------------------------------------------------------------------------------------

要进行交叉检查,我尝试使用相同的代码在array方法中直接访问switchTable,而不使用局部变量:

//concurrency stress test
@JCStressTest

//expected outcome (III_Result::toString)
@Outcome(id = "1, 1, 1", expect = Expect.ACCEPTABLE, desc = "Correctly initialized.")
@Outcome(id = ".+, .+, .+", expect = Expect.FORBIDDEN, desc = "Some Thread did see a not initialized array")

//test data
@State
public class EnumInitErr {

    int[] array;

    int[] switchTable() {
        if (array != null) {
            return array;
        } else {
            array = new int[1];
            array[0] = 1;
            return array;
        }
    }

    // each called only by one particular Thread, once per State instance
    @Actor public void test1(III_Result r) {
        r.r1 = switchTable()[0];
    }

    @Actor public void test2(III_Result r) {
        r.r2 = switchTable()[0];
    }

    @Actor public void test3(III_Result r) {
        r.r3 = switchTable()[0];
    }
}

这一次,我们得到了一些未初始化数组的结果:

... [output deleted] ...

RUN COMPLETE.

RUN RESULTS:
------------------------------------------------------------------------------------------------------------------------

*** INTERESTING tests
  Some interesting behaviors observed. This is for the plain curiosity.

  0 matching test results. 

*** FAILED tests
  Strong asserts were violated. Correct implementations should have no assert failures here.

  6 matching test results. 
  [FAILED] cfh.jcstress.EnumInitErr
    (JVM args: [-XX:+UnlockDiagnosticVMOptions, -XX:+StressLCM, -XX:+StressGCM])
  Observed state   Occurrences   Expectation  Interpretation                                              
         0, 0, 1           565     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 0         1.089     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 1         2.835     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 0           792     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 1         3.272     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 0         2.394     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    70.441.694    ACCEPTABLE  Correctly initialized.                                      

  [FAILED] cfh.jcstress.EnumInitErr
    (JVM args: [-XX:-TieredCompilation, -XX:+UnlockDiagnosticVMOptions, -XX:+StressLCM, -XX:+StressGCM])
  Observed state   Occurrences   Expectation  Interpretation                                              
         0, 0, 1           481     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 0           604     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 1         2.373     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 0           901     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 1         2.908     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 0         3.310     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    69.456.024    ACCEPTABLE  Correctly initialized.                                      

  [FAILED] cfh.jcstress.EnumInitErr
    (JVM args: [-XX:-TieredCompilation])
  Observed state   Occurrences   Expectation  Interpretation                                              
         0, 0, 1         1.332     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 0           948     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 1         4.212     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 0         1.071     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 1         5.216     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 0         5.052     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    64.580.410    ACCEPTABLE  Correctly initialized.                                      

  [FAILED] cfh.jcstress.EnumInitErr
    (JVM args: [-XX:TieredStopAtLevel=1])
  Observed state   Occurrences   Expectation  Interpretation                                              
         0, 0, 1         8.766     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 0        11.470     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 1        21.761     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 0        11.280     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 1        28.461     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 0        27.634     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    71.079.569    ACCEPTABLE  Correctly initialized.                                      

  [FAILED] cfh.jcstress.EnumInitErr
    (JVM args: [-Xint])
  Observed state   Occurrences   Expectation  Interpretation                                              
         0, 0, 1            77     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 0           117     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 1         2.417     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 0            83     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 1         2.291     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 0         1.789     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1     5.674.177    ACCEPTABLE  Correctly initialized.                                      

  [FAILED] cfh.jcstress.EnumInitErr
    (JVM args: [])
  Observed state   Occurrences   Expectation  Interpretation                                              
         0, 0, 1           785     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 0           804     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 1         2.717     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 0         1.250     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 1         4.041     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 0         5.500     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    63.510.464    ACCEPTABLE  Correctly initialized.                                      


*** ERROR tests
  Tests break for some reason, other than failing the assert. Correct implementations should have none.

  0 matching test results. 

*** All remaining tests
  Tests that do not fall into any of the previous categories.

  0 matching test results. 

------------------------------------------------------------------------------------------------------------------------

第一次使用jcstress,对任何错误表示歉意