volatile关键字(针对内存可见性)

news2024/9/22 23:56:29

一,示例

说明:创建两个线程,t1线程用来判断定义的flag变量是否等于0(等于0的话进入循环什么都不做),t2线程用来输入一个变量来修改flag的值;我们想要通过t2线程修改flag变量的值来达到跳出t1线程的循环的作用

代码如下:

import java.util.Scanner;

class Counter {
    public int flag = 0;
}

public class ThreadDemo5 {
    public static void main(String[] args) {
        Counter counter = new Counter();

        //t1线程用来进行判断
        Thread t1 = new Thread(() -> {
            while (counter.flag == 0) {
            }
        });

        //t2线程用来修改flag变量的值
        Thread t2 = new Thread(() -> {
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入一个整数:");
            counter.flag = scanner.nextInt();
        });
        t1.start();
        t2.start();
    }
}

通过结果我们考可以发现当我们将flag的值改为1时,t1线程并没有结束?这是为什么呢?

下面我们来主要探讨这个现象的主要原因并给出解决方案!

二,内存可见性问题

2.1 线程安全问题的原因:

1.抢占式执行,随机调度(根本原因,由操作系统内核决定,无法改变)

2.因为单个进程下的多个线程是资源共享的,所以多个线程修改同一个变量时会线程不安全

   多个线程修改不同的变量  没事

   一个线程修改同一个变量  没事

   多个线程读取同一个变量  没事

3.原子性

   如果修改操作是原子性的,就不会有线程安全问题

   如果修改操作是非原子性的就很大概率出现线程安全问题(上述示例的add操作就是非原子性         的,一个add操作分成了三个指令去执行)

   所以我们去避免线程安全的主要手段就是将非原子性的操作变成原子性---->加锁

4.内存可见性问题

    当一个线程在读取数据,一个线程在修改数据时就会出现线程安全问题(也就是常说的脏读问        题)

5.指令重排序(本质上是代码出现了bug)

2.2 内存可见性问题

1.上述示例的原因

上述示例也是一个典型的线程不安全的例子,造成这个现象的主要原因就是上述线程安全原因的第四点——内存可见性问题

对于while(counter.flag == 0)这条语句用汇编来理解可以看成两个操作,第一步:load;把内存中flag的值读取到寄存器中;第二步:cmp;把寄存器中的值和0作比较来判断是否进入循环。由于load的执行速度相对于cmp来说很慢,再加上反复load所读取的值都是一样的,于是编译器开始进行优化(觉得没有人再改flag变量了),只读取一次造成修改后的flag的值没有及时被读取成功。

2.什么是内存可见性问题?

一个线程针对一个变量进行读操作,另一个线程针对同一个变量进行写操作,此时读到的值不一定是修改之后的值,这就是内存可见性问题(上述t1线程读flag变量,t2线程修改flag变量);内存可见性问题最根本的原因就是编译器优化!

三,解决内存可见性问题(volatile关键字)

volatile关键字专门是用来修饰变量,不可以修饰方法,只需要在可能会发生改变的变量前加上volatile关键字即可,针对上述示例加上volatile代码之后:

import java.util.Scanner;

class Counter {
    public volatile int flag = 0;
}

public class ThreadDemo5 {
    public static void main(String[] args) {
        Counter counter = new Counter();

        //t1线程用来进行判断
        Thread t1 = new Thread(() -> {
            while (counter.flag == 0) {
                //System.out.println("hello thread");
            }
        });

        //t2线程用来修改flag变量的值
        Thread t2 = new Thread(() -> {
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入一个整数:");
            counter.flag = scanner.nextInt();
        });
        t1.start();
        t2.start();
    }
}

此时输入整数1之后发现整个进程随之结束;

当对flag变量加上volatile关键字之后,系统就会认为整个变量是异变的,就不会再进行编译器优化了,此时t1线程就可以感知到t2线程对flag变量的修改了(强制读取内存,虽然执行效率减低了一点,但是代码的准确性提高了)

四,volatile的原理

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

总结:

 1.volatile会强制读写内存,编译器此时不会进行优化,可以感知到线程对变量的修改;

2. volatile和synchronized都是解决线程安全问题的关键字,但是synchronized是针对使得原子性问题的关键字,而volatile是针对内存可见性问题的关键字。

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

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

相关文章

Educational Codeforces Round 140 (Rated for Div. 2)(A,B,D)

太久没写博客了,感觉做的题不自己写一遍思路总还是有点问题。。。又到了新年啦,cf的新年特效爱了爱了A. Cut the Triangle给出三角形的三个顶点坐标,问是否可以使用水平或者竖直线从任意一个顶点将三角形划为两部分。思路:易得知&…

研发协同利器:XState调研与应用

背景帖子详情是一个图文/视频混排、拥有大量长文本、大量交互和部分细节动效的页面,细节组件非常多,页面复杂度高。按以往的页面协作方式,会将一个个组件样式、组件数据和组件交互逻辑交给对应的开发同学完成,通过多人协同最终搭建…

【数据结构】C语言实现栈和队列

目录 一、栈 1、栈的概念及结构 2、如何实现栈 3、代码实现 3.1 栈的定义 3.2 栈中将要实现的函数 3.3 函数实现 二、队列 1、队列的概念及结构 2、如何实现队列 3、代码实现 3.1 队列定义 3.2 队列中将要实现的函数 3.3 函数实现 一、栈 1、栈的概念及结构 栈&am…

AI医药论文阅读-使用药物描述和分子结构从文献中提取药物-药物相互作用

202107Using drug descriptions and molecular structures for drug-drug interaction extraction from literature 使用药物描述和分子结构从文献中提取药物-药物相互作用 Bioinformatics. 2021.07 有代码 https://github.com/tticoin/DESC_MOL-DDIE 目录 202107Using dru…

2022亚太杯数学建模(补赛)DE题思路模型代码

占个位置吧,开始在本帖实时更新赛题思路代码,文章末尾名片获取!ABC题已更新 持续为更新参考思路 赛题思路 会持续进行思路模型分析,下自行获取。 D题思路: (比赛开始后第一时间更新) E题思…

面试官:海量请求下的接口并发解决方案,具体聊聊吧

设定一个场景,假如一个商品接口在某段时间突然上升,会怎么办? 生活中的例子来说,假设冰墩墩在当天晚上上热搜之后,迅速有十几万人去淘宝下单购买,此时并没有做好对该商品的缓存预热以及准备,如何…

【力扣刷题】day1-1、两数之和 2、两数相加

力扣刷题笔记day1 1,两数之和 题意 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元…

C++ · 入门 · 04 | 引用

啊我摔倒了..有没有人扶我起来学习.... 👱个人主页:《CGod的个人主页》\color{Darkorange}{《CGod的个人主页》}《CGod的个人主页》交个朋友叭~ 💒个人社区:《编程成神技术交流社区》\color{Darkorange}{《编程成神技术交流社区》…

STM32MP157驱动开发——Linux WIFI驱动

STM32MP157驱动开发——Linux WIFI驱动一、简介二、驱动开发1.wifi驱动添加与编译2.配置 USB 支持设备1)配置 USB 支持设备2)配置支持的 WIFI 设备3)配置支持 IEEE 802.114)使能 STAGING 配置3.设备树配置4.编译 wifi 驱动1&#…

05SpringCloudAlibaba负载均衡服务调用-Ribbon

目录 推荐与004SpringCloud-Ribbon_gh_xiaohe的博客-CSDN博客 对比观看 Ribbon概述 Ribbon官网https://github.com/Netflix/ribbon/wiki/Getting-Started Ribbon是什么 Ribbon也进入维护模式 Ribbon能干什么 LB(负载均衡) 一句话:…

冲击港交所:百果园书写水果连锁运营默示录

卖水果是一门古老的生意,但是长期以来并不受资本重视。一是因为产品口味难以标准化、鲜度要求高、流通环节易损耗、质量控制难度大。二是因为交易主体多,进出壁垒小,经济学中往往将其定义为完全竞争市场,难以出现龙头企业和超额利…

AI与艺术——图像生成模型是否能挑战人类艺术?

在2018年末的佳士得纽约拍卖场上,一件名为《爱德蒙贝拉米肖像》拍出了43.25万美元的价格,从绘画艺术风格来看,这是一幅有着很明显的印象派主义痕迹的作品。 印象派是19世纪中叶在法国兴起的一种艺术运动。印象派艺术家们拒绝了艺术(“美术”…

Webpack中的文件指纹

1. 什么是文件指纹? 文件指纹就是打包后输出的文件名的后缀,主要用来对修改后的文件做版本区分。 2. 文件指纹有哪几种? 1. Hash:和整个项目的构建相关,只要项目文件有修改,整个项目构建的 hash 值就会更…

CSRF与XSS攻防知识点总结

本章节将用于详细总结记录,跨站脚本攻击XSS(cross site script)与 跨站请求伪造CSRF(cross site request forgery)这两种常见的浏览器安全的攻防手段。本章节会介绍两种攻击的概念,以及相关手段有哪些&…

常见的电动两轮车 BMS 架构

1、摘要 近年来,随着新国标的施行,以及平衡车,滑板车,共享电单车等新应用场景的出现,促使电动两轮车市场迎来了新的发展热潮。 锂电池因为具有能量密度高,循环次数多等优点而逐渐替代铅酸电池&#xff0c…

Spire.XLS for Java 12.12.4 2022-12-30 Version

Spire.XLS for Java 12.12.4 Spire.XLS for Java是一个专业的 Java Excel API,Ω578867473使开发人员无需使用 Microsoft Office 或 Microsoft Excel即可创建、管理、操作、转换和打印 Excel工作表。 Spire.XLS for Java 支持旧的 Excel 97-2003 格式(.…

Java中main函数里的String[] args详解

1)概念 在开始学习 Java 时都会被要求记住主方法(main)的写法,就像以下: public static void main(String[] args) { };public static void main(String args[]) { }; 这里做如下说明&#xff…

什么是 A/B 实验,为什么要开 A/B 实验?

更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群 1、什么是 A/B 实验 A/B 实验也被称为 A/B 测试,实验的基本思路是在线上流量中取出一小部分(较低风险),完全随机地分…

【机器学习】minHash最小哈希原理及其应用

目录1 前言2 哈希函数的定义3 miniHash函数4 miniHash的例子5 miniHash数学原理6 miniHash的应用7 参考文献1 前言 在数据结构中学过哈希概念以及哈希在内存中的应用,在实际的应用问题中哈希技术也应用十分广泛如在推荐系统以及图神经网络技术中,所以在此…

Java多线程之死锁问题,wait和notify

文章目录一. synchronnized 的特性1. 互斥性2. 可重入性二. 死锁问题1. 什么是死锁2. 死锁的四个必要条件3. 常见的死锁场景及解决3.1 不可重入造成的死锁3.2 循环等待的场景哲学家就餐问题(多个线程多把锁)两个线程两把锁三. Object类中提供线程等待的方法1. 常用方法2. wait和…