JUC:java内存模型(如何保证?可见性、原子性、有序性)

news2024/11/28 0:29:51

文章目录

  • java内存模型
    • 可见性
      • 解决方法
    • 原子性
    • 有序性
      • 流水线技术
    • 模式之Balking(犹豫)

java内存模型

JMM 即 Java Memory Model,它定义了主存、工作内存抽象概念,底层对应着 CPU 寄存器、缓存、硬件内存、CPU 指令优化等。

JMM 体现在以下几个方面 :

  • 原子性 - 保证指令不会受到线程上下文切换的影响
  • 可见性 - 保证指令不会受 cpu 缓存的影响
  • 有序性 - 保证指令不会受 cpu 指令并行优化的影响

可见性

按照如下代码,一般我们都会想,run改成false,那么线程t1也就运行结束了,但是并没有运行结束,而是无线运行下去了。

这就算因为可见性导致的。

@Slf4j(topic = "c.Test5")
public class st5 {
    static boolean run = true;

    public static void main(String[] args) throws InterruptedException {

        Thread t = new Thread(()->{
            while(run){

            }
        });
        t.start();

        sleep(1);
        run = false; // 线程t不会如预想的停下来
    }
}

在一开始t1线程是在主内存中获取 run 值。
在这里插入图片描述

随着 while 次数一直获取次数增多,JIT 编译器会将 run 的值缓存至自己工作内存中的高速缓存中,减少对主存中 run 的访问,提高效率。

在这里插入图片描述

但是,会带来一个问题,就是主存和内存中的值不一致,当主存内容修改,t1 线程任然从内存中获取run值,就会读到旧的内容,从而一直停不下来。

在这里插入图片描述

解决方法

加volatile, 它可以用来修饰成员变量和静态成员变量,可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的最新值,线程操作 volatile 变量都是直接操作主存。

volatile static boolean run = true;

当然我么之前用synchronized上锁,让共享变量在锁代码块中操作,也是可以保证可见性的,不然我么之前的的代码不就都不对了么。

不过synchronized毕竟比较重还要创建monitor,而volatile是更加轻量级的。

原子性

volatile只能保证读取到最新值,而不能解决原子性问题。

比如线程t1读取到了值x = 0,要进行++操作时,正好切换了,切换到了线程t2了,而t2也读取x = 0, 并对于x进行了–操作返回给内存进行更新,然后又切换回线程t2,t2该进行++操作,并且赋值x更新到内存。

原本一加一减应该变回0,但经过这种线程切换的情况,x最后为1,显然不符合我们的预期,因为破坏了读写的原子性。

所以在保证原子性时,还是利用synchronized和ReentrantLock编写代码时对共享变量读写保证其原子性。

有序性

指令重排

JVM 会在不影响正确性的前提下,可以调整语句的执行顺序。

比如i和j同时++,因为互不影响,谁先执行都一样,所以jvm可能的执行顺序就是i++,j++也可以是j++,i++。

流水线技术

流水线技术是一种并行计算的方式,它类似于生产流水线,将一个复杂的任务分解为一系列的子任务,并且这些子任务可以并行地执行,每个子任务的输出作为下一个子任务的输入,从而实现整体任务的并行处理。

在计算机科学中,流水线技术通常用于优化处理速度,特别是对于那些可以被分解为多个相互独立阶段的任务。通过流水线技术,可以将一个任务分解为多个阶段,每个阶段由专门的处理单元来执行,这样就可以同时处理多个阶段,从而提高整体处理速度。

流水线技术常见于处理器的设计中,例如现代CPU中的指令流水线。在指令流水线中,CPU将指令执行分解为多个阶段,比如取指阶段、解码阶段、执行阶段等等,每个阶段由专门的硬件单元来执行,这样就可以实现多条指令的并行执行,从而提高CPU的吞吐量。

简单来说啊,在不改变结果的情况下,把一个任务,拆成多个,重排序,一起执行。

在这里插入图片描述

// 可以重排
int a = 10; // 指令1
int b = 20; // 指令2
System.out.println( a + b );

// 不能重排
int a = 10; // 指令1
int b = a - 5; // 指令2

指令重排出现的问题

int num = 0;
boolean ready = false;

// 线程1 执行此方法
public void actor1(I_Result r) {
    if(ready) {
        r.r1 = num + num;
    } else {
        r.r1 = 1;
    }
}

// 线程2 执行此方法
public void actor2(I_Result r) { 
    num = 2;
    ready = true; 
}

r.r1最后可能为0,但是概率非常非常非常小,我们自己测不出来的。

原因线程2执行方法指令重排,先执行了ready = true,这时切换线程1,执行了r.r1 = num + num,然后线程2才执行了num = 2。那么最后r.r1就等于0.

解决方法
还是加上volatile即可,可以防止指令重排序。
volatile会保证 顺序性 和 可见性。

模式之Balking(犹豫)

犹豫模式,一个线程在执行某个操作之前会检查某个条件,如果条件不满足,则放弃执行,直接返回。这个模式的核心思想就是避免重复执行某个操作,当发现已经有其他线程或本线程已经执行了相同的操作时,就直接结束当前线程的执行。
Balking 模式通常适用于一些需要进行资源共享或状态同步的场景。当多个线程需要对共享资源进行访问或修改时,可以使用 Balking 模式来确保资源的正确使用,避免出现竞态条件或资源浪费。

举个简单的例子,假设有一个线程池,多个线程需要从线程池中获取任务执行。在获取任务之前,每个线程会检查线程池中是否还有可用的任务,如果有,则取出任务执行,如果没有,则放弃执行,直接返回。这样就可以避免多个线程同时尝试获取任务导致的竞态条件,同时也可以避免线程在没有可执行任务时进行无效的等待,提高了线程池的效率。

public class MonitorService {
    
    // 用来表示是否已经有线程已经在执行启动了
    private volatile boolean starting;
    
    public void start() {
        log.info("尝试启动监控线程...");
        synchronized (this) {
            if (starting) {
                return;
            }
            starting = true;
        }
        
        // 真正启动监控线程...
    }
}

这里volatile是可以不加的,synchronized已经保证了可见性,不过,通常建议还是添加 volatile 关键字来明确地表达你的意图,即使这样做可能会带来一些微小的性能开销。

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

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

相关文章

从0到1:兼职招聘小程序开发笔记(一)

可行性分析 兼职招聘小程序:为雇主和求职者提供便利的平台,旨在帮助雇主招聘兼职员工,并让求职者寻找合适的兼职工作。提供简单、快捷的方式来匹配兼职岗位和候选人,节省了招聘和求职的时间和精力。其主要功能模块包括&#xff1…

C语言数据结构易错知识点(6)(快速排序、归并排序、计数排序)

快速排序属于交换排序,交换排序还有冒泡排序,这个太简单了,这里就不再讲解。 归并排序和快速排序都是采用分治法实现的排序,理解它们对分支思想的感悟会更深。 计数排序属于非比较排序,在数据集中的情况下可以考虑使…

详细分析Mysql中的STR_TO_DATE基本知识(全)

目录 前言1. 基本知识2. Demo3. 实战Demo4. Sql彩蛋4.1 LPAD函数4.2 SUBSTRING_INDEX函数 5. Java彩蛋 前言 对于该知识点,主要因为数据库类型为String(类似2024-03-26),放置于后端操作后,需要自定义比较,…

LLaMA-Factory微调(sft)ChatGLM3-6B保姆教程

LLaMA-Factory微调(sft)ChatGLM3-6B保姆教程 准备 1、下载 下载LLaMA-Factory下载ChatGLM3-6B下载ChatGLM3windows下载CUDA ToolKit 12.1 (本人是在windows进行训练的,显卡GTX 1660 Ti) CUDA安装完毕后&#xff0c…

HCIP---MGRE和GRE实验

一、配置ip R1: [R1]int g0/0/0 [R1-GigabitEthernet0/0/0]ip add 192.168.1.254 24 [R1-GigabitEthernet0/0/0]int s4/0/0 [R1-Serial4/0/0]ip add 15.1.1.1 24 [R1]ip route-static 0.0.0.0 0 15.1.1.5 R2: [R2]int g0/0/0 [R2-GigabitEthernet0/0/0]ip add 192.168.2.2…

GROBID库文献解析

1. 起因 由于某些原因需要在大量的文献中查找相关内容,手动实在是太慢了,所以选择了GROBID库进行文献批量解析 2. GROBID介绍 GROBID是一个机器学习库,用于将PDF等原始文档提取、解析和re-structuring为结构化的XML/TEI编码文档&#xff0…

C++第十四弹---模板初阶

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】 目录 1、泛型编程 2、函数模板 2.1、函数模板的概念 2.2、函数模板的格式 2.3、函数模板的原理 2.4、函数模板的实例化 2.5、模板参数的匹配原则 …

【学习】如何成为资深的软件测试工程师“大神”?

一个优秀的软件测试工程师不仅需要有深厚的技术知识和经验,还需要有良好的沟通能力、分析能力和问题解决能力。总的来说,一个"大神"一样的软件测试工程师应该是一个全面的技术专家,同时还需要有出色的沟通和问题解决能力&#xff0…

Mac 版 IDEA 中配置 GitLab

一、安装Git 在mac终端输入Git检测指令&#xff0c;可以通过git命令查看Git是否安装过&#xff0c;如果没有则会弹出安装按钮&#xff0c;如果安装过则会输出如下信息。 WMBdeMacBook-Pro:~ WENBO$ git usage: git [--version] [--help] [-C <path>] [-c namevalue][--…

ubuntu23.10配置RUST开发环境

系统版本: gcc版本 下载rustup安装脚本: curl --proto https --tlsv1.2 https://sh.rustup.rs -sSf | sh下载完成后会自动执行 选择默认安装选项 添加cargo安装目录到环境变量 vim ~/.bashrc 默认已添加 使用环境变量立即生效 source ~/.bashrc 执行rust开发环境,在终端输入…

深度剖析:计算机集群在大数据体系中的关键角色和技术要点

什么是计算机集群&#xff1f; 计算机集群是一组相互连接的计算机&#xff08;服务器&#xff09;&#xff0c;它们协同工作以完成共同的任务。集群中的每个计算机节点都可以独立运行&#xff0c;但它们通过网络连接在一起&#xff0c;以实现更高的可靠性、性能和可扩展性。 典…

给虚拟机配置静态IP并使用FileZIlla在虚拟机和Windows之间传输文件(ssh和ftp两种方法)

一、配置操作系统网络 &#x1f338;下面的步骤主要是配置虚拟机的静态IP&#xff0c;方便后续用 FikeZilla 在windows和虚拟机之间传输文件&#xff08;否则用默认的ip分配方案为 DHCP ,每一次开机时的ip都是有可能不同的,这样就会导致每次远程连接都需要查看ip地址.&#xf…

Python | 非规则矩形投影添加斑马线边框

前言 在地图绘制领域&#xff0c;非规则投影的示例相对较少&#xff0c;通过几个python的示例可以更好地理解如何在不同投影类型和边界形状下绘制地图。 以下提供了一系列示例&#xff0c;演示了如何在地图中添加非规则边界和边框。这些示例涵盖了不同的投影类型和边界形状&a…

element-ui 表单校验,失去焦点/框内值改变,校验

前提&#xff1a;在el-form表单中&#xff0c;框中有值&#xff0c;失去焦点或者框内值改变的时候&#xff0c;校验提示&#xff0c;依旧没有消失el-select校验失效问题 之前el-select&#xff0c;trigger时候用的“blur”,导致失效&#xff0c;现在 el-select 统一改为"c…

PyTorch深度学习实战(40)——零样本学习(Zero-Shot Learning)

PyTorch深度学习实战&#xff08;40&#xff09;——零样本学习 0. 前言1. 零样本学习2. 实现零样本学习模型2.1 模型分析2.2 构建零样本学习模型 小结系列链接 0. 前言 零样本学习 (Zero-Shot Learning) 是一种机器学习方法&#xff0c;旨在解决传统监督学习中&#xff0c;当…

pulsar存在大量消费未ack的原因

问题起源&#xff1a; 某产品灰度上线后&#xff0c;从pulsar服务端监控发现存在一种现象&#xff1a;消费但未ack的信息不断增加&#xff0c;直到3000左右就稳定下来了且消费速度为0&#xff0c;但不清楚这3000是怎么来的&#xff0c;因为代码是消费到立马ack的&#xff1b; …

格雷希尔G10系列L150A和L200A气动快速连接器,在新能源汽车线束线缆剥线后的气密性测试密封方案

线束线缆在很多用电环境都有使用&#xff0c;比如说新能源汽车&#xff0c;从电池包放电开始&#xff0c;高低压、通讯都开始进行工作&#xff0c;线束在连接的地方需要具有较高的气密性和稳定性&#xff0c;才能保证车辆在不同环境下能够正常的运行。 线束在组装铜鼻子前需要剥…

【Oracle篇】expdp/impdp高效完成全部生产用户的全库迁移(第四篇,总共四篇)

☘️博主介绍☘️&#xff1a; ✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、Linux&#xff0c;也在扩展大数据方向的知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章&#xff0c;并且也会默默的点赞收藏加关注❣️❣️…

基于SpringBoot的游戏商城系统的设计与实现(论文+源码)_kaic

目录 1前言 1.1研究的背景及意义 1.2国内外的研究状况和发展趋势 2需求分析 2.1系统需求分析 2.1.1技术可行性 2.1.2经济可行性 2.1.3操作可行性 2.2系统的开发环境 2.2.1 Springboot框架 2.2.2 数据库Mysql 2.2.3 IntelliJ IDEA平台 2.2.4 Mybatis和MyBatis-plus 2.2.5 前端框…

火鸟门户同城模块

同城活动 同城活动是指在同一城市举办的活动&#xff0c;可以是多种类型&#xff0c;例如&#xff1a; 聚会&#xff1a;朋友聚会、同学聚会、兴趣爱好聚会等。展览&#xff1a;艺术​​展览、科技展览、文化展览等。演出节目&#xff1a;演唱会、音乐会、戏剧表演等。比赛项…