并发编程三大特性:3、有序性

目录

3.1 什么是有序性

3.2 as-if-serial

3.3 happens-before

3.4 volatile


3.1 什么是有序性

在Java中,.java文件中的内容会被编译,在执行前需要再次转为CPU可以识别的指令,CPU在执行这些指令时,为了提升执行效率,在不影响最终结果的前提下(满足一些要求),会对指令进行重排。

指令乱序执行的原因,是为了尽可能的发挥CPU的性能。

Java中的程序是乱序执行的。

Java程序验证乱序执行效果:

static int a,b,x,y;

public static void main(String[] args) throws InterruptedException {
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
        a = 0;
        b = 0;
        x = 0;
        y = 0;

        Thread t1 = new Thread(() -> {
            a = 1;
            x = b;
        });
        Thread t2 = new Thread(() -> {
            b = 1;
            y = a;
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        if(x == 0 && y == 0){
            System.out.println("第" + i + "次,x = "+ x + ",y = " + y);
        }
    }
}

单例模式由于指令重排序可能会出现问题:

扫描二维码关注公众号,回复: 16435862 查看本文章

线程可能会拿到没有初始化的对象,导致在使用时,可能由于内部属性为默认值,导致出现一些不必要的问题

private static volatile MiTest test;

private MiTest(){}

public static MiTest getInstance(){
    // B
    if(test  == null){
        synchronized (MiTest.class){

            if(test == null){
                // A   ,  开辟空间,test指向地址,初始化
                test = new MiTest();
            }
        }
    }
    return test;
}

3.2 as-if-serial

as-if-serial语义:

不论指定如何重排序,需要保证单线程的程序执行结果是不变的。

而且如果存在依赖的关系,那么也不可以做指令重排。

// 这种情况肯定不能做指令重排序
int i = 0;
i++;

// 这种情况肯定不能做指令重排序
int j = 200;
j * 100;
j + 100;
// 这里即便出现了指令重排,也不可以影响最终的结果,20100

3.3 happens-before

具体规则:
  1. 单线程happen-before原则:在同一个线程中,书写在前面的操作happen-before后面的操作。
  2. 锁的happen-before原则:同一个锁的unlock操作happen-before此锁的lock操作。
  3. volatile的happen-before原则: 对一个volatile变量的写操作happen-before对此变量的任意操作。
  4. happen-before的传递性原则: 如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。
  5. 线程启动的happen-before原则:同一个线程的start方法happen-before此线程的其它方法。
  6. 线程中断的happen-before原则:对线程interrupt方法的调用happen-before被中断线程的检测到中断发送的代码。
  7. 线程终结的happen-before原则:线程中的所有操作都happen-before线程的终止检测。
  8. 对象创建的happen-before原则:一个对象的初始化完成先于他的finalize方法调用。
JMM只有在不出现上述8中情况时,才不会触发指令重排效果。

不需要过分的关注happens-before原则,只需要可以写出线程安全的代码就可以了。

3.4 volatile

如果需要让程序对某一个属性的操作不出现指令重排,除了满足happens-before原则之外,还可以基于volatile修饰属性,从而对这个属性的操作,就不会出现指令重排的问题了。

volatile如何实现的禁止指令重排?

内存屏障概念。将内存屏障看成一条指令。

会在两个操作之间,添加上一道指令,这个指令就可以避免上下执行的其他指令进行重排序。

猜你喜欢

转载自blog.csdn.net/weixin_42717648/article/details/130727709