(十) 共享模型之内存【有序性】

news2024/11/13 11:27:46
JVM 会在不影响正确性的前提下,可以调整语句的执行顺序
这种特性称之为『 指令重排 』,多线程下『指令重排』会影响正确性。为什么要有重排指令这项优化呢?从 CPU 执行指令的原理来理解一下吧

一、原理之指令级并行(了解)(P141)

1. 鱼罐头的故事

  

2. 名词

(1)Clock Cycle Time
主频的概念大家接触的比较多,而 CPU Clock Cycle Time (时钟周期时间),等于主频的倒数,意思是 CPU 能够识别的最小时间单位,比如说 4G 主频的 CPU Clock Cycle Time 就是 0.25 ns,作为对比,我们墙上挂钟的 Cycle Time 1s
例如,运行一条加法指令一般需要一个时钟周期时间
(2)CPI
有的指令需要更多的时钟周期时间,所以引出了 CPI Cycles Per Instruction )指令平均时钟周期数
(3)IPC
IPC Instruction Per Clock Cycle) 即 CPI 的倒数,表示每个时钟周期能够运行的指令数
CPU 执行时间
程序的 CPU 执行时间,即我们前面提到的 user + system 时间,可以用下面的公式来表示
程序 CPU 执行时间 = 指令数 * CPI * Clock Cycle Time

3. 指令重排序优化

在不改变程序结果的前提下,这些指令的各个阶段可以通过 重排序 组合 来实现 指令级并行 ,这一技术在 80's 中叶到 90's 中叶占据了计算架构的重要地位。

 指令重排的前提是,重排指令不能影响结果

二、诡异的结果

public class ConcurrencyTest {

    int num = 0;
    boolean ready = false;
    @Actor
    public void actor1(I_Result r) {
        if(ready) {
            r.r1 = num + num;
        } else {
            r.r1 = 1;
        }
    }

    @Actor
    public void actor2(I_Result r) {
        num = 2;
        ready = true;
    }

}

 

三、解决方法

volatile 修饰的变量,可以禁用指令重排

四、原理之 volatile

volatile 的底层实现原理是内存屏障,Memory Barrier(Memory Fence)

(1)对 volatile 变量的写指令后会加入写屏障

(2)对 volatile 变量的读指令前会加入读屏障

1. 如何保证可见性

写屏障(sfence)保证在该屏障之前的,对共享变量的改动,都同步到主存当中

而读屏障(lfence)保证在该屏障之后,对共享变量的读取,加载的是主存中最新数据 

 

2. 如何保证有序性

写屏障会确保指令重排序时,不会将写屏障之前的代码排在写屏障之后
读屏障会确保指令重排序时,不会将读屏障之后的代码排在读屏障之前
还是那句话,不能解决指令交错:
(1)写屏障仅仅是保证之后的读能够读到最新的结果,但不能保证读跑到它前面去
(2)而有序性的保证也只是保证了本线程内相关代码不被重排序

 

3. 以著名的 double-checked locking 单例模式为例

public final class Singleton {
    private Singleton() { }
    private static Singleton INSTANCE = null;
    public static Singleton getInstance() {
        if(INSTANCE == null) { // t2
            // 首次访问会同步,而之后的使用没有 synchronized
            synchronized(Singleton.class) {
                if (INSTANCE == null) { // t1
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}
很关键的一点:第一个 if 使用了 INSTANCE 变量,是在同步块之外

INSTANCE 没完全受到 synchronized 的保护,所以 INSTANCE  可能会指令重排

4. double-checked locking 解决

INSTANCE 使用 volatile 修饰即可,可以禁用指令重排,但要注意在 JDK 5 以上的版本的 volatile 才会真正有效
读写 volatile 变量时会加入内存屏障( Memory Barrier Memory Fence)),保证下面两点:
(1)可见性
1️⃣ 写屏障( sfence )保证在该屏障之前的 t1 对共享变量的改动,都同步到主存当中
2️⃣而读屏障( lfence )保证在该屏障之后 t2 对共享变量的读取,加载的是主存中最新数据
(2)有序性
1️⃣写屏障会确保指令重排序时,不会将写屏障之前的代码排在写屏障之后
2️⃣读屏障会确保指令重排序时,不会将读屏障之后的代码排在读屏障之前
(3)更底层是读写变量时使用 lock 指令来多核 CPU 之间的可见性与有序性

 

五、happens-before

happens-before 规定了对共享变量的写操作对其他线程的读操作可见,它是可见性与有序性的一套规则总结,抛开以下  happens-before 规则,JMM 并不能保证一个线程对共享变量的写,对于其他线程对该共享变量的读可见。

(1)线程解锁 m 之前对变量的写,对于接下来对 m 加锁的其它线程对该变量的读可见

public class Ces {

    static int x;
    static Object m = new Object();

    public static void main(String[] args) {

        new Thread(() -> {
            synchronized (m) {
                x = 10;
            }
        }, "t1").start();
        new Thread(() -> {
            synchronized (m) {
                System.out.println(x);
            }
        }, "t2").start();
    }
}

(2)线程对 volatile 变量的写,对接下来其它线程对该变量的读可见

public class Ces {

    volatile static int x;

    public static void main(String[] args) {

        new Thread(()->{
            x = 10;
        },"t1").start();
        new Thread(()->{
            System.out.println(x);
        },"t2").start();
    }
}

(3)线程 start 前对变量的写,对该线程开始后对该变量的读可见

public class Ces {

    static int x;

    public static void main(String[] args) {

        x = 10;
        new Thread(()->{
            System.out.println(x);
        },"t2").start();
    }
}

(4)线程结束前对变量的写,对其它线程得知它结束后的读可见(比如其它线程调用 t1.isAlive() 或 t1.join()等待它结束)

public class Ces {

    static int x;

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            x = 10;
        },"t1");
        t1.start();
        
        t1.join();
        System.out.println(x);
    }
}

(5)线程 t1 打断 t2interrupt)前对变量的写,对于其他线程得知 t2 被打断后对变量的读可见(通过 t2.interrupted t2.isInterrupted)

public class Ces {

    static int x;

    public static void main(String[] args)  {
        Thread t2 = new Thread(()->{
            while(true) {
                if(Thread.currentThread().isInterrupted()) {
                    System.out.println(x);
                    break;
                }
            }
        },"t2");
        t2.start();
        new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            x = 10;
            t2.interrupt();
        },"t1").start();
        while(!t2.isInterrupted()) {
            Thread.yield();
        }
        System.out.println(x);
    }
}

(6)对变量默认值(0falsenull)的写,对其它线程对该变量的读可见

(7)具有传递性,如果 x hb-> y 并且 y hb-> z 那么有 x hb-> z ,配合 volatile 的防指令重排,有下面的例子

public class Ces {

    volatile static int x;
    static int y;

    public static void main(String[] args)  {
        new Thread(()->{
            y = 10;
            x = 20;
        },"t1").start();
        new Thread(()->{
            // x=20 对 t2 可见, 同时 y=10 也对 t2 可见
            System.out.println(x);
        },"t2").start();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/60553.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[附源码]Python计算机毕业设计Django企业人事管理系统

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,…

如何使用HTML制作个人网站( web期末大作业)

📂文章目录一、👨‍🎓网站题目二、✍️网站描述三、📚网站介绍四、🌐网站演示五、⚙️ 网站代码🧱HTML结构代码💒CSS样式代码六、🥇 如何让学习不再盲目七、🎁更多干货一…

Linux安装mysql

1、 查看是否已经安装 Mysql rpm -qa | grep mysql 如果你查看出来有东西,可以使用下面命令将其删除 rpm -e 文件名 2 、下载官方 Mysql 包 wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm 如果安装有提示:Cannot…

HTML5期末大作业:北京旅游网页设计制作(1页) 简单静态HTML网页作品 我的旅游网页作业成品 学生旅游网站模板

👨‍🎓学生HTML静态网页基础水平制作👩‍🎓,页面排版干净简洁。使用HTMLCSS页面布局设计,web大学生网页设计作业源码,这是一个不错的旅游网页制作,画面精明,排版整洁,内容…

gin 集成 Swagger

前言 一个好的项目工程,必然离不开一个好的 API 文档,如果要自己编写 API 文档,维护起来比较困难,而且难以保证一致性,因此我们要自动生成在线接口文档。 swaggo swagger 在 java 里面,是一个非常流行的…

后渗透之日志分析实验

目录 一、实验项目名称 二、实验目的 三、实验内容 四、实验环境 五、实验步骤 六、实验结果 七、实验总结 一、实验项目名称 后渗透之日志分析实验 二、实验目的 1.掌握meterpreter进行端口转发的方法 2.掌握网站日志的分析方法 三、实验内容 针对目标网站服务器…

AlphaFold2源码解析(7)--模型之Evoformer

AlphaFold2源码解析(7)–模型之Evoformer 这篇文章我们主要药讲解AlphaFold2的Evoformer的代码细节。 Evoformer Stack 该网络有一个双塔结构,在MSA堆栈中具有轴向的自我注意;在Pair堆栈中具有三角形的乘法更新和三角形的自我注意;以及外积…

Windows多线程编程

一、 实验内容或题目: 以多线程编程的方式完成: 1)随机生成一个数组,求其平均值 2)随机生成一个数组,求其最大值 3)随机生成一个数组,求其最小值 二、 实验目的与要求:…

Kaggle Feedback Prize 3比赛总结:如何高效使用hidden states输出(2)

比赛链接:https://www.kaggle.com/competitions/feedback-prize-english-language-learning 在Kaggle Feedback Prize 3比赛总结:如何高效使用hidden states输出(2)中介绍了针对last layer hidden state的各种pooling的方法。 在利用Transformer类的预…

Vue学习:Hello小案例

使用Vue的目的:构建用户界面(需要使用容器 摆放这个界面的内容) favicon.ico:1 GET http://127.0.0.1:5500/favicon.ico 404 (Not Found) 没有页签图标 在者服务器中 http://127.0.0.1:5500没有/favicon.ico 强制刷新网页:s…

3大经典分布式存储算法

文章目录1、背景2、算法2.1 分布存储之哈希取余算法2.2 分布式存储之一致性哈希算法2.3 分布式存储之哈希槽算法1、背景 一个经典的面试题目:1~2亿条数据需要缓存,请问如何设计这个方案? 回答:单台单机肯定不可能&…

Musical Christmas Lights——一个圣诞树灯光✨随音乐节奏改变的前端开源项目

文章目录前言视频介绍项目截图项目地址项目源码以上就是本篇文章的全部内容,将你编写好的项目分享给你的朋友们或者那个TA吧!制作不易,求个三连!❤️ 💬 ⭐️前言 今天博主在刷短视频时😐,朋友推…

VMware 虚拟机系统 与 win10 共享文件夹问题的解决

环境描述 本地:Win10 64位 VMware Workstation Pro 16 虚拟机,安装的 ubuntu 20.04 文件夹共享 win10 与 虚拟机的 ubuntu 共享文件夹,之前低版本的 VMware ,安装 VMware Tools,并且 win10 端设置好工作目录后&…

秒级使网站变灰,不改代码不上线,如何做到?

注意:文本不是讲如何将网站置灰的那个技术点,那个技术点之前汶川地震的时候说过。 本文不讲如何实现技术,而是讲如何在第一时间知道消息后,更快速的实现这个置灰需求的上线。 实现需求不是乐趣,指挥别人去实现需求才…

广域网技术——SR-MPLS隧道保护技术

目录 TI-LFA FRR保护技术 LFA FRR R-LFA FRR TI-LFA FRR Anycast FRR技术 Host-Standby技术 VPN FRR技术 SR-MPLS防微环技术 场景一 SR本地正切防微环 场景二 SR本地回切防微环 场景三 SR远端正切防微环 场景四 SR远端回切防微环 TI-LFA和防微环的对比 TI-LFA FRR…

41. set()函数:将可迭代对象转换为可变集合

41. set()函数:将可迭代对象转换为可变集合 文章目录41. set()函数:将可迭代对象转换为可变集合1. set( )函数的作用2. set( )函数的语法3. set函数创建空集合4. set函数的参数只能是可迭代对象4.1 将字符串转换为集合4.2 set( )函数的参数不能为整数4.3…

MIT 6.S081 Operating System Lecture8 (非常随意的笔记)

系列文章目录 文章目录系列文章目录Page FaultCOPY ON WRITEPage Fault eager allocation 通常,因为应用程序无法非常准确地估计自己要增加的内存有多少,所以通常申请的内存会比真实要使用的内存要多。 在XV6中,sbrk的实现默认是eager alloc…

基于粒子群算法优化的lssvm回归预测-附代码

基于粒子群算法优化的lssvm回归预测 - 附代码 文章目录基于粒子群算法优化的lssvm回归预测 - 附代码1.数据集2.lssvm模型3.基于粒子群算法优化的LSSVM4.测试结果5.Matlab代码摘要:为了提高最小二乘支持向量机(lssvm)的回归预测准确率&#xf…

【C++】stack/queue/list

文章目录注意事项1 emplace 与 push 的区别一、stack(栈)(先进后出、【头部插入、删除】、不许遍历)1 基本概念(栈是自顶向下(top在下),堆是向上)2 stack 常用接口(构造函数、赋值操…

[蓝牙 Mesh Zephyr]-[005]-Key

[蓝牙 Mesh & Zephyr]-[005]-Key 1. Keys Mesh Profile specification 定义了 2 种key:application keys (AppKey)和 network keys(NetKey)。AppKeys 用于保护 upper transport layer 的通信安全,Net…