多线程--内存可见性问题以及volatie关键字

大语言模型(LLM)在组合优化中的新突破:LLM-LNS与FunSearch性能对比 本文深入探讨了大语言模型(LLM)在组合优化中的创新应用,重点对比了LLM-LNS与FunSearch两种方法的性能差异。研究表明,LLM-LNS在物流路径规划、资源调度等场景中展现出显著优势,其双层自演化架构能有效平衡探索与利用,相比传统方法提升效率约43%。文章还提供了实践选择指南,帮助开发者根据具体需求选用合适的技术方案。 阅读详情

内存可见性也是造成线程安全问题的原因之一

一.什么是可见性?

可见性指, 一个线程对共享变量值的修改,能够及时地被其他线程看到. ​

所以内存安全性问题就是一个线程对共享变量值的修改 其他线程不能够看到

看下面一串代码


public class Demo {
    public static int flag = 0;

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            while (flag == 0) {
                //这里什么也不做
            }
            System.out.println("thread1 线程结束");
        });

        Thread thread2 = new Thread(() -> {
            //针对flag进行修改
            Scanner scanner = new Scanner(System.in);
            System.out.println("输入要修改的flag值:");
            flag = scanner.nextInt();
        });

        thread1.start();
        thread2.start();
    }

}

 以上代码线程1会在线程2修改之前flag一直等于0,while会无限循环, 线程2对flag进行输入修改,按理来所当线程2修改完flag之后线程2结束,线程1也会跳出循环线程结束

但是真正的结果↓

可以看到无论输入什么,程序都不会终止

通过JDK的jconsole来看线程的状态

 

可以看到线程2已经结束 线程1的状态仍然在运行 意思也就是while让然还在循环 线程2对flag值的修改并被影响到线程1 很明显这就是一个bug 线程安全问题,一个线程读取,一个线程修改,修改线程的值,并没有被读取的线程读取到

这就是内存可见性问题

上述JMM模型可见上篇文章方便后续理解 

 

 二.出现的原因

其实就是编译器优化再搞鬼

我们写的的代码 编译器把.java文件编译成.class字节码文件 再到JVM中运行

研究JDK的开发人员 希望通过让编译器,JVM,甚至是操作系统之间对程序员写的代码自动进行优化,会在你原有的逻辑不变的情况下,对你的代码进行调整,使程序的效率提高,在日常开发中提升的效率是明显可见的,也是非常有必要的

编译器虽然是优化操作,是能够保证逻辑不变,尤其是在多线程的程序中,编译器的判断可能出现失误,就可能导致编译器的优化,使优化后的逻辑和优化前的逻辑出现细节上的偏差 上述代码就是这种情况

分析代码:

JVM对于while循环执行这么多次的flag的操作 发现始终都是0既然结果都一样既然还要反复执行那么多次,没有必要,就把读读取内存的操作优化成读取寄存器这样的操作(把内存的值读取到寄存器中,获取再load(将数据从内存加载到寄存器)不再重新读内存,直接从寄存器里来取) 

于是等到很多秒后,用户真正的输入新的值,真正修改flag,此时t1线程就感知不到(编译器优化是的线程1的读取操作不是真正的内存读取)

三.解决问题

解法1:

在while循环中加入sleep

本来while循环转的飞起,1s中几千万次,上亿次 但是加上sleep(1)之后循环次数就会大幅度降低,引入sleep之后,sleep消耗的时间相比于上面的load flag的操作就高了不知道多少 sleep占用1ms此时优化与不优化flag无足轻重

加上sleep由于sleep消耗的时间远大于load flag的操作 就没有必要优化 所以不会产生内存可见性问题 这是一种解决方法 但是太鸡肋了 sleep太消耗性能了

方法二:使用volatile关键字

volatile 修饰的变量, 能够保证 "内存可见性". ​

代码在写入 volatile 修饰的变量的时候, ​
• 改变线程工作内存中volatile变量副本的值​
• 将改变后的副本的值从工作内存刷新到主内存
代码在读取 volatile 修饰的变量的时候, ​
• 从主内存中读取volatile变量的最新值到线程的工作内存中​
• 从工作内存中读取volatile变量的副本​

简单来说:

加上 volatile , 强制读写内存. 速度是慢了, 但是数据变的更准确了. ​线程2修改的数据 线程1就能及时看到了

private volatile static int flag2 = 0;
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            while (flag2 == 0) {

            }
            System.out.println("thread1 线程结束");
        });

        Thread thread2 = new Thread(() -> {
            //针对flag进行修改
            Scanner scanner = new Scanner(System.in);
            System.out.println("输入要修改的flag值:");
            flag2 = scanner.nextInt();
        });

        thread1.start();
        thread2.start();
    }

 

可见都能解决问题

volatile只能解决可见性问题 至于原子性问题还是要用synchronized(锁)来解决

 

DeepSmote:一种深度SMOTE方法 DeepSmote 一种全新的将深度学习与Smote相结合的过采样方法! 目前在许多领域已经被使用,值得大家关注学习! 阅读详情

相关推荐

三相逆变器双pi控制器参数如何调节_光伏逆变器短路特性简析

1前言短路故障相对于其他故障类型来说是比较常见的,不同的设备的短路故障,大了讲都一样,细了说各有千秋,今天我们主要聊聊光伏逆变器的短路特征。电力系统中的电源,传统意义是指的是并入电力系统的同步发电机。但随着分布式发电技术的发展,各种形式的发电装置并入电力系统。按照分布式电源并网的方式,我们大致可以分为以下三类:➤通过旋转电机和变流器并联并网的电源,主要指双馈风力发电系统;➤仅仅通过变流器...

weixin_39901404的博客 1732

volatile详解

volatile的三大特性,以及应用场景

weixin_46120888的博客 5790

深入LSM6DSL的FIFO连续模式:基于STM32和MEMS库的数据流高效处理指南

本文深入探讨了LSM6DSL的FIFO连续模式,结合STM32硬件平台和ST官方MEMS库,提供了高效的数据流处理方案。通过优化I2C通信时序、中断驱动设计以及动态ODR调整,实现低功耗数据采集与处理,显著提升嵌入式传感器应用的性能和续航能力。

weixin_30387799的博客 348

Volatile详解,太详细了-JMM java内存模型

Volatile可能是面试里面必问的一个话题吧,对他的认知很多朋友也仅限于会用阶段,今天我们换个角度去看看。 先来跟着丙丙来看一段demo的代码: 你会发现,永远都不会输出有点东西这一段代码,按道理线程改了flag变量,主线程也能访问到的呀? 为会出现这个情况呢?那我们就需要聊一下另外一个东西了。 JMM(JavaMemoryModel) JMM:Java内存模型,是java虚拟机规范中所定义的一种内存模型,Java内存模型是标准化的,屏蔽掉了底层不同计算机的区别(注意这个跟JVM

没有伞的孩子必须努力奔跑!—小林 1万+

CloudCompare 多窗口联动显示点云

CloudCompare支持多窗口联动显示多个点云数据,可以用来比对原始点云数据与语义分割后的结果。 一、创建多个视窗 打开CloudCompare后会默认有一个视图,我们要同时打开两个数据就需要再创建一个视图,选择菜单栏里的3D Views,在下拉菜单中点击new,这样就创建了一个新的视图,这时3D Views的下拉菜单中有3D View 1和3D View 2两个视图,然后点击3D Views下拉菜单中的Title,这时在数据展示区就会出现两个并列的窗口。 二、分别加载点云数据 将数据分别拖进两个

zhaoguanhua的博客 2739

ESP32-C3模组上跑通HTTPS分次传输文件(1)

ESP32-C3模组上跑通HTTPS分次传输文件(1)

phmatthaus的专栏 760

Volatile是什么?

概述Volatile 是 Java 中一个非常有用的关键字,用于保证多线程环境下共享变量的可见性和一致性。可见性在多线程环境中,当一个线程修改了共享变量的值时,其他线程可能无法立即看到最新的值。这是因为每个线程都有自己的工作内存,其中包含对共享变量的副本。当一个线程修改共享变量时,只有该线程的工作内存中的副本才会被更新,而其他线程工作内存中的副本仍然是旧的值。Volatile 可以保证所有线程都能看到共享变量的最新值。当一个线程修改了共享变量的值时,JVM 会将该值刷新到主内存中。

byteinit的专栏 3475

Ardupilot EKF3核心算法《开篇》

Ardupilot EKF3核心算法本专栏重点,透彻,详细的分析了ardupilot的EKF3,保证是你看到的最完美的EKF3资料,相信你的选择不会错误。网上EKF资料真是太多,说真的没有一篇讲解好的。!!

陌城烟雨 1413

volatile的作用和原理

Volatile关键字的作用主要有如下两个:1. 线程的可见性:当一个线程修改一个共享变量时,另外一个线程能立刻读到这个修改的值。2. 顺序一致性:禁止指令重排序。

乡村非主流2020的博客 5587

2026年全国青少年信息素养大赛算法应用主题赛C++赛项【决赛】模拟卷1:文末附答案

2026年全国青少年信息素养大赛算法应用主题赛C++赛项【决赛】模拟卷1:文末附答案

王老师青少年编程 1178

【C2000系列DSP的Bootloader详解】如何打包源文件为库文件且加密仅授权使用,一步步教你如何实现

如果想要在 TI 的 CCS 开发环境中为 C2000 系列 MCU 生成 lib 库文件,并对这些库文件进行加密保护,确保只有授权方(持有密钥)才能调用和使用,以此保护源代码的知识产权。

昔时扬尘处 177

java并发编程 ||深入解析volatie,可见性与内存屏障

上一章我们说了多线程带来的不安全性,详见 java并发编程 ||深入理解synchronized,锁的升级机制,那么除了不安全性,它还有一些什么问题呢。 话不多说,我们先来看一个例子 /** * @Author Dark traveler * @Note 我心净处,何处不是西天。 * @Descrption * @E-Mail : 1029149772@qq.com * @Date...

qq_40111437的博客 593

Allegro输出光绘文件规范

添加钻孔表的具体步骤为:1.通过屏幕右边的Visibility选项的Views列表,将Drill层打开2.将Visibility选项中的PIN和Via选项都选中,见下图所示: 参数设好之后关闭NC Drill/Parameters窗口,输出数控机床钻孔文件的命令菜单为:Manufacture=>NC=>NC Drill…,打开的窗口如下: 输出数控机床钻孔文件的具体步骤为: 上图是光绘输出的层控制窗口,还有一个窗口是输出参数的控制窗口,我们通过点击上图的第二个列表项General Parameters来

axingxiansen的博客 2808

Java多线程之内存可见性

一、JAVA内存模型简介 JAVA Merory  Model描述了JAVA程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节。 所有的变量都保存在主内存中,但是每个线程都有自己的独立工作内存,保存该线程使用到的变量的一个副本。 两条规定 1.线程对共享变量的操作只能在独立的工作内存中进行,不能在主内存中直接读写; 2.不

沈鸿斌的博客 2739

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

7、共享模型之内存(可见性、原子性、有序性;volatie原理以及使用案例、happens-before)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言Java 内存模型可见性退不出的循环解决方法可见性和原子性用volatile实现我们之前说的两阶段终止模式同步模式 Balking有序性鱼罐头的故事诡异的结果解决方法volatile原理如何保证可见性如何保证有序性doubled-checked locking 问题doubled-checked locking 解决happens-before习题balking 模式习题线程安全单例习题 前言 之前讲解的 Monitor 主.

m0_45364328的博客 203

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

什么时候需要使用volatile关键字

想必大家平时都见过volatile关键字,可是大家知道什么时候需要使用volatile关键字吗? 直接看下面代码: int a = 100; while (a == 100) { // code } 这段程序编译时,如果编译器发现程序始终没有企图改变a的值,那它可能就会优化这段代码,变成while(true)的死循环使得程序执行的更快,然而这种优化有时候会变成过度优化,编译器有时候可能没有意识到程序会改变a的值,却做了这种优化导致程序没有产生预期的行为。 这里为了产生预期的行为,需要阻止编译器做这种优化

weixin_45905650的博客 2905

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Java并发(一):volatie与synchronized实现原理

上下文切换如何减少上下文切换CAS算法ABA问题Java并发机制底层实现Volatile关键字线程可见性防止指令重排序volatie的实现缓存行自动填充synchronized关键字synchronized底层实现java代码层级上字节码层级上Java对象内存布局对象头锁状态无锁偏向锁轻量级锁重量级锁三种锁的比较 上下文切换 多线程是通过CPU给每个线程分配CPU时间片来实现的,所以本质上CPU只能执行一个线程而不能执行多个,多线程其实就是CPU在不断地切换线程去执行,让外界感觉像是多个在同时运行 多线程.

GDUT_Trim的博客 3873

java volatie用法_Java--volatile关键字的作用与用法

volatile 关键字作用是,使系统中所有线程对该关键字修饰的变量共享可见,可以禁止线程的工作内存对volatile修饰的变量进行缓存。volatile 2个使用场景:1.可见性:Java提供了volatile关键字来保证可见性。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。而普通的共享变量不能保证可见性,因为普通共享变...

weixin_42161450的博客 251

Volatile关键字的作用

Volatile关键字的作用主要有如下两个:1.线程的可见性:当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。2. 顺序一致性:禁止指令重排序。

Admans的专栏 1223

说说关键字volatile

volatile关键字详讲1可见性什么是JMM volatiele关键字的作用有两个: 1:保证变量的可见性 2:禁止指令重排 1可见性 什么叫做可见性,这里需要先说下java内存模型(JMM) 什么是JMM 先来看一张图

qq_37376024的博客 405

[Java]-volatile关键字(变量不可见,原子性)

本章思维导图 并发编程下,多线程访问变量的不可见性问题 指多个线程访问共享变量,会出现一个线程修改变量的值后,其他线程看不到最新值的情况 代码示例: package VolatileTest; public class VolatileDemo extends Thread{ private boolean flag=false; ...

renboyu010214的博客 593

Java volatile关键字深入JVM理解

volatile关键字修饰的变量可以保证可见性 变量用volatile修饰得时候,当一个线程修改了变量,其他线程可以立即读到修改后得值,适用于一写多读得多线程场景,多线程对变量有复杂操作禁止使用,容易引起线程间并发的一些问题,当一个线程修改了volatile修饰的变量时,cpu运算后会立即写到共享主存中,其他cpu读取的时候会先将自己独有内存的变量副本置为不可用,直接从共享主存中读取数据,总结:对...

qq_43456816的博客 280

关于多线程下的 volatile 一词的理解

关于多线程下的 volatile 一词的理解 1.首先我给出该关键词的俩方面用处,接下来展开详述。 作用: 1.1 在多线程访问下保证了线程之间的可见性。 1.2 被该关键词修饰的变量,限制了JVM的指令重排序的发生。 详述: 1.保证了线程之间的可见性 再多线程情况下,例如:多个线程对一资源进行访问时,如果某一个线程对该资源进行了读取,并修改,那么另外的线程不能马上发现该变量的改变。进而继续操作该变量。可能这些话有些难理解。那么接下来用一个例子来说明: 伪代码: 共100张票,同时2个窗口出售。 cl

zhangruijie的博客 975

Java 并发编程—volatile 关键字

volatile 关键字 volatile 作用 volatie 用于修饰变量,它有两个作用。 它可以保证变量在多线程之间是可见的,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。 禁止指令重排序。 volatile 保证变量的可见性 先来看一下 volatile 保证变量在线程之间的可见性问题,在下面这段代码中,isContinuePrint共享变量,printThread...

六号表哥 182

【JavaEE】多线程之线程安全(volatile篇),wait和notify

多线程的线程安全问题,volatile,wait和notify的详细讲解~

weixin_69417428的博客 649

多线程线程安全问题

死锁产生的必要条件,避免死锁(避免嵌套,约定加锁的顺序);内存可见性引起的线程安全问题,原因编译器优化

captain376的博客 216
上一篇: 多线程-线程安全问题及解决方法
下一篇: 多线程--阻塞队列and生产消费者模型
刘 大 望
博客等级 码龄2年 896粉丝 61原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值