Summarized the two instruction rearrangement scenarios and wrote code to implement them.
Scenario 1: Two threads assign values back and forth to different variables to verify whether instruction re-arrangement occurs. Scenario 2:
Singleton mode is a lazy mode. DCL+volatile can ensure that no accidents occur, but volatile is not added here intentionally to verify whether instruction re-arrangement occurs. Platoon leads to unexpected situations
import org.junit.Test;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.*;
public class InstructReorderTest {
int x = 0, y = 0, a = 0, b = 0;
@Test
public void test() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(2);
long start = System.nanoTime();
for (long i = 0; i < Long.MAX_VALUE; i++) {
x = y = a = b = 0;
CountDownLatch latch = new CountDownLatch(2);
Runnable r1 = () -> {
a=1;x=b;latch.countDown();};
Runnable r2 = () -> {
b=1;y=a;latch.countDown();};
// 当发生指令重排的时候,以上两行代码的ab与xy就可能交换了顺序. 如果xy同时=0则可证明以上两行代码同时发生了指令重排
executorService.submit(r1);
executorService.submit(r2);
latch.await(1, TimeUnit.SECONDS);
if (x + y == 0) {
long end = System.nanoTime();
System.err.println("x y 同时为0了. i: " + i + ", cost in ms: " + ((end - start) / 1_000_000));
// x y 同时为0了. i: 25037, cost in ms: 692
return;
}
}
}
// 验证单例中的指令重排
// 没跑出来,执行了超过千万次,都没有发生预期的指令重排,重写了逻辑,92万次发生了预期的指令重排
// o is null, i: '927155', cost: '34006'ms
// o is null, i: '1612447', cost: '20890'ms
// o is null, i: '1128129', cost: '16097'ms
// o is null, i: '10873976', cost: '133588'ms
// o is null, i: '15425452', cost: '204436'ms
@Test
public void test_singleton() throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(2);
long s = System.currentTimeMillis();
Callable r2 = () -> T.getInstance().o;
for (int i = 0; i < Integer.MAX_VALUE; i++) {
T.t = null;
List<Future> futures = new LinkedList<>();
for (int j = 0; j < 2; j++) {
futures.add(executorService.submit(r2));
}
for (Future future : futures) {
if (null == future.get()) {
long e = System.currentTimeMillis();
System.out.printf("o is null, i: '%s', cost: '%s'ms\n", i, (e - s));
return;
}
}
if (i % 10_0000 == 0) {
System.out.println("i: " + i);
}
}
}
static class T {
Object o;
public T(Object o) {
this.o = o;
}
static T t;
static T getInstance() {
if (null == t) {
synchronized (T.class) {
if (null == t) {
t = new T(new Object());
}
}
}
return t;
}
}
}