【JVM】---重排序

在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。

一、重排序介绍

重排序可能会导致多线程程序出现内存可见性问题。(工作内存和主内存,编译器处理器重排序导致的可见性)。
重排序会导致有序性问题,程序的读写顺序和内存的读写顺序不一样(编译器处理器重排序,内存缓冲区(是处理器重排序的内容))。

public class ReorderInMultiThread {
    int a = 0;
    boolean flag = true;
    //写线程
     private void writer(){
         //下面的赋值,由于两个行代码没有依赖关系,则可能会重排序
         a = 1;
         flag = true;
     }
     //读线程
    private void reader(){
         if(flag){
             int i = a* a;
         }
    }
}

-------对于上面的代码,如果是单线程的,先执行writer(),在执行reader(),这样是没问题。但多线程中,由于wirter()中方法的两行代码是没有依赖关系的,那么可能会重排序。会导致reader()方法出现错误。

二、解决重排序问题

(1) 内存屏障

内存屏障是一个屏障指令,使CPU对屏障指令之前和之后的内存操作结果都是一样的,相当于一种约束。用于控制特定条件下的重排序和内存可见性问题。

(2) as-if-serial语义

不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变。编译器,runtime 和处理器都必须遵守as-if-serial语义。

(3) 遵守happern-before原则

java内存模型提供了8种happen-before规则,帮助实现原子性可见性和有序性,在执行程序的时候查看是否满足happen-before的8种规则,如果满足的话就能够保证线程安全。
---------------是判断线程是否存在竞争,线程是否安全的依据。

程序顺序原则,一个线程内要按照代码的顺序执行,保证语义的串行性
锁规则,先解锁才能获取锁。
volatile规则,对volatile修饰的变量的写操作发生在volatile变量的读操作之前。访问volatile的变量时候强制从主内存中进行读取,volatile修饰的变量被修改之后会被强制的刷新到主内存中。所以说volatile修饰的变量保证了可见性
线程启动规则,线程的start()方法最先执行,线程A在线程B执行start()方法之前对共享变量的修改对线程B可见
线程终止规则,线程的所有操作优于线程的终结,Thread.join()方法的作用是等待当前线程终止,线程B在终止之前修改了共享变量,执行完成后,线程B对共享变量的修改将对线程A可见
线程中断规则,对线程interrupt中断方法的调用发生在被中断的线程检测到中断事件的发生之前
对象终结规则,对象的构造函数执行完成先于finalize()方法
传递性,线程A生在线程B之前,线程B发生在线程C之前,则线程A发生在线程C之前

(4) 编程中如何保证happern-before原则

使用同步操作:同步操作满足全序关系的,所以一定满足偏序关系。
同步操作方法有:锁的获取与释放【如Lock、synchronized】、对volatile变量的读和写。

(5) 保证happern-before原则–volatile和Synchronized

//    //变量是在方法区,大家都可见的
//    static int x = 0, y = 0;
//    static int a = 0, b = 0;
    /*
     * volatile方案【这种方案就是插入内存屏障】
     * 下面的调用目的是给x、y赋值,所以只需要x和y插入内存屏障
     */
//    static volatile int x = 0, y = 0;
//    static int a = 0, b = 0;
    /**
     * synchronized方案【锁的获取方案,插入内存屏蔽】
     */
      static int x = 0, y = 0;
      static int a = 0, b = 0;
    private void way1(){
        Thread one = new Thread(new Runnable() {
            @Override
            public void run() {
                a = 1; //step1
                x = b;//step2
            }
        });
        Thread other = new Thread(new Runnable() {
            @Override
            public void run() {
                b = 1; //step3
                y = a; //step4
            }
        });
        one.start();
        other.start();
        //两个线程会对值分别赋值,但由于重排序问题,那么会出现不同结果
    }
    private void way2(){
        Thread one = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (ReorderInMultiThread2.this){
                    a = 1; //step1
                    x = b;//step2
                }
            }
        });
        Thread other = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (ReorderInMultiThread2.this){
                    b = 1; //step3
                    y = a; //step4
                }
            }
        });
        one.start();
        other.start();
        //synchronized是加的类锁,不同线程之间是占用等待。
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DreamBoy_W.W.Y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值