首页 > 其他分享 >多线程|volatile的使用

多线程|volatile的使用

时间:2023-08-29 18:33:39浏览次数:38  
标签:load Thread cmd t1 flag volatile 使用 多线程

一、内存可见性问题

先来看如下代码

class MyCounter{
public int flag = 0;
}
public class ThreadDemo22 {
public static void main(String[] args) {
MyCounter myCounter = new MyCounter();
Thread t1 = new Thread(() -> {
while(myCounter.flag == 0){

}
try{
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("t1循环结束");
});
Thread t2 = new Thread(() -> {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个数:");
myCounter.flag = scanner.nextInt();
});
t1.start();
t2.start();
}
} 执行结果如下:

 我们期望修改flag的值为1后,t1线程输出“t1线程结束循环”后结束循环,然而结果是修改flag的值为1之后,t1仍然没有结束循环,这种情况就叫做内存可见性问题。

上述的代码中,存在一个线程读,一个线程修改的情况,从汇编语言的角度来理解,读和修改分两步操作:

load,把内存中flag的值读到寄存器中;

cmd,把寄存器里flag的值与0比较,根据比较的结果来决定下一步怎么执行。                                                                                                                                                                                                                                     

由于CPU对寄存器的操作比对内存的操作快了几个数量级,因此,相较于cmd来说,load的速度很慢。load执行一次,cmd可能已经执行很多很多次了,在cmd执行的过程中,获取到的flag的值都是一样的,JVM有一个很大胆的设定,不再真正的执行load操作了,判定没人修改flag的值,干脆只load一次。

虽然JVM预判没有人会修改flag的值,实际上还是会修改flag的值,这时就需要程序员手动干预,给flag加上volatile关键字,加上volatile关键字表示被修饰的这个变量是“易变的”,也就是在告诉操作系统,每次都要重新进行load操作,因为这个变量的值随时都可能会变化,可不能只操作一次load。

我们用volatile修饰flag,如下。

 做了上述的调整后,看看代码的结果:

 此时代码执行结果就如我们期望的那样。

注意volatile只能修饰变量,volatile也不能保证原子性。

标签:load,Thread,cmd,t1,flag,volatile,使用,多线程
From: https://www.cnblogs.com/xbyss/p/17665600.html

相关文章

  • 开源.NetCore通用工具库Xmtool使用连载 - HTTP请求篇
    【Github源码】《上一篇》介绍了Xmtool工具库中的XML操作类库,今天我们继续为大家介绍其中的HTTP请求类库。在现如今的软件需求场景中,HTTP网络请求几乎是开发过程中必然会使用的功能;而系统自带的HTTPClient对象使用起来并不是那么容易和友好,因此我们对其进行了二次封装成了一个We......
  • 8.使用PKG打包node Express
    1.全局安装依赖包PKGnpminstall-gpkg2.下载自己电脑装的nodejs对应版本的打包文件https://github.com/vercel/pkg-fetch/releases 3.下载后放入以下目录C:\Users\用户名\.pkg-cache\v3.4(没有版本目录的自己新建,这里版本也要对应电脑装的)记得修改下文件名称:fetched-v18.......
  • 使用samba创建共享文件夹(Linux - Windows)
    1.安装samba有些Linux已经自带了samba$sudoaptinstallsamba-y2.配置防火墙详情请参考https://zhuanlan.zhihu.com/p/508580900,因本人使用的是MX23,不是很会设置,且无其它安全需求,故直接关闭防火墙3.配置samba$sudovim/etc/samba/smb.conf#按个人需要可以备份......
  • C#使用 SAPscript的方法
    主要參照文檔https://wenku.baidu.com/view/a28b71adcf22bcd126fff705cc17552707225ed2.html?_wkts_=1693296465595&bdQuery=AxSAPFEWSELib.AxGuiApplication.GetScriptingEngine里面讲得比较清楚,但是最大的问题是代码不能Copy1)首先必须安装SAPGUI,当然SAP服务器,账号,密码这些......
  • 小程序组件使用
    在小程序中,父子组件之间可以通过属性和事件来进行数据的传递。使用属性传递数据:父组件可以通过在子组件的标签上绑定属性,将数据传递给子组件。子组件可以在组件的properties属性中定义接收父组件传递的属性,并在组件内部使用。父组件通过改变绑定的属性的值,可以实现向子组......
  • 使用fetch获取wttr.in天气预报
    使用fetch获取wttr.in天气预报在https://wttr.in/可以看到当前ip的天气预报情况,在网页上想快速取得数据并组装为想显示的样式就需要自行处理了这里我们来尝试实现一个LED时间显示功能天气预报 样式表clock.css@font-face{font-family:'UnidreamLED';/*anametobeused......
  • Window 10/11 使用网银时的浏览器问题
    在微软已经弃用IE的今天,很多国内的企业网银还是基于IE的,因此经常无法正常使用。本文介绍Edge浏览器与国内网银配合使用的方法。Edge使用IE模式若不使用IE模式,则直接无法进入相关网银的网站。解决步骤:打开Edge浏览器-右上角三个点-设置-默认浏览器:将【允许在......
  • qt使用QMetaObject::invokeMethod异步调用或QTimer::singleShot解决很久才能显示界面
    https://blog.csdn.net/weixin_43935474/article/details/124922897//Load();//QMetaObject::invokeMethod(this,"Load",Qt::QueuedConnection);//无参数,QueuedConnection表示异步调用,等主线程QMetaObject::invokeMethod(this,"Load",Qt::QueuedConnectio......
  • 【2.0】Docker安装与使用
    【一】卸载Docker如果之前已经安装了Docker,可以按照以下步骤进行卸载:yumremovedockerdocker-commondocker-selinuxdocker-enginerm-rf/var/lib/docker【二】更新yum包到最新版本执行以下命令将yum包更新至最新版本:sudoyumupdate【三】安装所需软件包执行......
  • 32位数字电位器AD5228使用及调试总结
    一概念什么是数字电位计?  数字电位器(DigitalPotentiometer)亦称数控可编程电阻器,是一种代替传统机械电位器(模拟电位器)的新型CMOS数字、模拟混合信号处理的集成电路。数字电位器由数字输入控制,产生一个模拟量的输出。依据数字电位器的不同,抽头电流最大值可以从几百微安到几个......