【java】java如何证明java发生了指令重排序

在这里插入图片描述

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=0351157次循环x =0 y=0483627次循环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 这个是无法锁住的,会报错,因此这里随便整个空操作。

猜你喜欢

转载自blog.csdn.net/qq_21383435/article/details/111461579