【多线程:volatile】有序性

简介: 【多线程:volatile】有序性

【多线程:volatile】有序性

01.介绍

什么是有序性

在不改变程序结果的前提下 指令可以在各个阶段通过重排序和组合来实现指令级并行。
注意
重排序指令不能影响结果

为什么这样做

cpu处理过程

在cpu处理一个指令是包含一系列工序的,包含了:取指 译码 地址生成 执行 回写,这五步 但是这五步之间是可以交错运行的,也就是一个指令译码的时候另一个指令可以取指。
例子1

    x=a;
    y=b;


这样的执行效率显然更加的快,且在语句之间没有关系时 执行顺序可以随便,也就是可能先执行y=b 再执行x=a,不影响结果

例子2

    x=a;
    c=x+1;
    y=b;

这段代码是否可以套用上述的图片,是不行的。
理论上应该对应的是下面这幅图

但实际上这段代码被优化后可能是

    x=a;
    y=b;
    c=x+1;

对应的图片

这样做的原因是c=x+1需要x的值确定 也就是一定要等待x的指令全部完成之后才能执行c的指令,但是按照最初的顺序y=b是在最后的 也就是他也需要在x指令执行完才能运行 但是y=b和前两个指令没有任何关系,所以进行了优化 使得在x运行期间 y也可以运行,提高了并发性,使单独一个指令运行的情况减少。
总结:例子二的这种情况就是指令重排序,在单线程情况下没有什么影响,但是在多线程情况下就有可能出问题。

02.多线程情况下的指令排序

public class Disorder {
    private static int x=0,y=0;
    private static int a=0,b=0;
    //无论这两个线程怎么排列组合 如果没有重排序,一定不会存在x=0 且 y=0
    public static void main(String[] args) throws InterruptedException {
        int i=0;
        //不停的死循环
        while (true){
            i++;
            x=0;y=0;
            a=0;b=0;
            Thread one= new Thread(()-> {
                //前后没有依赖关系 a=1和 x=b
                a=1;
                x=b;
            });
            Thread other = new Thread(()-> {
                //如果不存在乱序,b=1一定在y=a前面
                b=1;
                y=a;
            });
            one.start();other.start();
            one.join();other.join();
            String result = "第"+i+"次("+x+","+y+")";
            if(x==0 && y==0){
                System.err.println(result);
                break;
            }else{
                System.out.println(result);
            }
        }

    }
    public static void shortWait(long interval){
        long start = System.nanoTime();
        long end;
        do{
            end=System.nanoTime();
        }while (start+interval>=end);
    }

}

结果

.
.
.
.
第181799次(0,1)
第181800次(0,1)
第181801次(0,1)
第181802次(0,1)
第181803次(0,1)
第181804次(0,0)

解释
我们分析上述代码,考虑到多线程的情况,理论上x,y最多组合的情况就只有3种(1,0) (1,1) (0,1),但是我们却发现了第四种情况(0,0) 要出现这种情况一定是x=b在a=1之前,y=a在b=1之前,很显然这种情况就只有指令重排序了

03.禁止指令重排序

很简单就是在x,y前加一个volatile,作用是保证在x,y之前的代码都比它先执行,例如a=1;x=b;保证a=1一定在x=b之前执行,同理y也是这样,同时把之前的变量包括自己的值同步到主存中。同时需要给a,b前加上volatile保证a b的可见性 要不然可能会出现a b已经更新到主存 但读取的还是本地内存
补充:最后这段解释可能大家会理解的不清楚,具体解释涉及到volatile的原理,下一个文章会讲。

目录
相关文章
|
3月前
|
存储 SQL 缓存
揭秘Java并发核心:深度剖析Java内存模型(JMM)与Volatile关键字的魔法底层,让你的多线程应用无懈可击
【8月更文挑战第4天】Java内存模型(JMM)是Java并发的核心,定义了多线程环境中变量的访问规则,确保原子性、可见性和有序性。JMM区分了主内存与工作内存,以提高性能但可能引入可见性问题。Volatile关键字确保变量的可见性和有序性,其作用于读写操作中插入内存屏障,避免缓存一致性问题。例如,在DCL单例模式中使用Volatile确保实例化过程的可见性。Volatile依赖内存屏障和缓存一致性协议,但不保证原子性,需与其他同步机制配合使用以构建安全的并发程序。
71 0
|
4月前
|
缓存 安全 算法
Java面试题:如何通过JVM参数调整GC行为以优化应用性能?如何使用synchronized和volatile关键字解决并发问题?如何使用ConcurrentHashMap实现线程安全的缓存?
Java面试题:如何通过JVM参数调整GC行为以优化应用性能?如何使用synchronized和volatile关键字解决并发问题?如何使用ConcurrentHashMap实现线程安全的缓存?
47 0
|
6月前
|
存储 安全 Java
并发编程知识点(volatile、JMM、锁、CAS、阻塞队列、线程池、死锁)
并发编程知识点(volatile、JMM、锁、CAS、阻塞队列、线程池、死锁)
106 3
|
3月前
|
缓存 Java
【多线程面试题二十三】、 说说你对读写锁的了解volatile关键字有什么用?
这篇文章讨论了Java中的`volatile`关键字,解释了它如何保证变量的可见性和禁止指令重排,以及它不能保证复合操作的原子性。
|
4月前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
78 1
|
4月前
|
设计模式 缓存 安全
Java面试题:工厂模式与内存泄漏防范?线程安全与volatile关键字的适用性?并发集合与线程池管理问题
Java面试题:工厂模式与内存泄漏防范?线程安全与volatile关键字的适用性?并发集合与线程池管理问题
59 1
|
4月前
|
微服务
多线程内存模型问题之在单例模式中,volatile关键字的作用是什么
多线程内存模型问题之在单例模式中,volatile关键字的作用是什么
|
4月前
线程可见性和关键字volatile
线程可见性和关键字volatile
|
4月前
|
算法 安全 Java
Java面试题:解释JVM中的堆内存分代收集策略,并讨论年轻代和老年代的特点,描述Java中的线程池,并解释线程池的优点,解释Java中的`volatile`关键字的作用和使用场景
Java面试题:解释JVM中的堆内存分代收集策略,并讨论年轻代和老年代的特点,描述Java中的线程池,并解释线程池的优点,解释Java中的`volatile`关键字的作用和使用场景
47 0
|
6月前
|
安全 Java 编译器
Java多线程基础-6:线程安全问题及解决措施,synchronized关键字与volatile关键字(一)
线程安全问题是多线程编程中最典型的一类问题之一。如果多线程环境下代码运行的结果是符合我们预期的,即该结果正是在单线程环境中应该出现的结果,则说这个程序是线程安全的。 通俗来说,线程不安全指的就是某一代码在多线程环境下执行会出现bug,而在单线程环境下执行就不会。线程安全问题本质上是由于线程之间的调度顺序的不确定性,正是这样的不确定性,给我们的代码带来了很多“变数”。 本文将对Java多线程编程中,线程安全问题展开详细的讲解。
102 0