当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$
的分配?如果可能的话,可能存在竞争情况,其中一个线程看到数组,而另一个线程仍在修改元素。
答案 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
,对任何错误表示歉意