1.概述
package com.java.memory.commondsort;
/**
* @author: chuanchuan.lcc
* @date: 2020-12-20 19:37
* @modifiedBy: chuanchuan.lcc
* @version: 1.0
* @description: 指令重排序验证
*/
public class CommonSort {
private static int x = 0, y = 0;
private static int a = 0, b = 0;
public static void main(String[] args) throws InterruptedException {
int i = 0;
for (; ; ) {
i++;
x = 0;
y = 0;
a = 0;
b = 0;
Thread one = new Thread(new Runnable() {
@Override
public void run() {
a = 1;
x = b;
}
});
Thread two = new Thread(new Runnable() {
@Override
public void run() {
b = 1;
y = a;
}
});
one.start();
two.start();
one.join();
two.join();
if (x == 0 && y == 0) {
String result = "第" + i + "次循环" + "x =" + x + " y=" + y;
System.out.println(result);
}
}
}
}
代码如上,按照上面的逻辑一般情况下不会出现x=0 y=0 ,如果发送了,那么说明发生了指令重排序
第146611次循环x =0 y=0
第351157次循环x =0 y=0
第483627次循环x =0 y=0
因为指令重排序,所以我们在构建单例的时候
@Data
public class Money {
private Long money1;
private Long money2;
private Long money3;
public Money() {
this.money1 = 1000L;
try {
Thread.sleep(2L);
money2 = 2000L;
Thread.sleep(2L);
money3 = 3000L;
}catch (Exception e){
}
}
}
public class MoneyTest {
private Money money;
public Money getInstance() {
if (money == null) {
synchronized (Money.class) {
if (money == null) {
try {
Thread.sleep(1L);
} catch (InterruptedException e) {
e.printStackTrace();
}
money = new Money();
}
}
}
return money;
}
private static Long x = 0L, y = 0L, z = 0L;
@Test
public void moneyTest() throws InterruptedException {
int i = 0;
for (; ; ) {
i++;
x = 0L;
y = 0L;
z = 0L;
Thread one = new Thread(new Runnable() {
@Override
public void run() {
x = getInstance().getMoney1();
}
});
Thread two = new Thread(new Runnable() {
@Override
public void run() {
y = getInstance().getMoney2();
z = getInstance().getMoney3();
}
});
one.start();
two.start();
one.join();
two.join();
if (x == 1000L && y == 0L) {
String result = "第" + i + "次循环" + "x =" + x + " y=" + y;
System.out.println(result);
}
}
}
}
这里要加入一个 volatile,为什么加入呢?
private volatile Money money;
因为在第一个线程的时间,假设New对象的时候对象创建了一半,突然指令重排序了,然后第二个线程来的时候,
public Money getInstance() {
if (money == null) {
....
}
return money;
}
判断不为空,然后就返回了,然后就使用了,然后就会导致第二个线程使用的是对象的默认值都是0,当然这种情况很难发生,但是理论是是可以发生的,这里我想测试出来这种现象,没有测试出来。
所以这里需要添加volatile实现内存的可见性,后端使用的实际是内存屏障,内存屏障每种虚拟机的实现都不同
这个图中的lock很强大,是锁总线,这里为什么是
lock :add1$0.0
这里是什么意思呢?lock锁住总线,然后给寄存器中执行 +0操作,相当于空操作。这是汇编指令,既然是汇编为啥不用汇编的空操作nop这个空指令操作呢?因为lock nop 这个是无法锁住的,会报错,因此这里随便整个空操作。