Java多线程共享变量控制volatile

1. volatile实现可见性(jdk 1.5后)

1. 可见性

如果一个线程对共享变量值的修改,能够及时的被其他线程看到,叫做共享变量的可见性。如果一个变量同时在多个线程的工作内存中存在副本,那么这个变量就叫共享变量

volatile如何实现可见性?

volatile变量每次被线程访问时,都强迫线程从主内存中重读该变量的最新值,而当该变量发生修改变化时,也会强迫线程将最新的值刷新回主内存中。这样一来,不同的线程都能及时的看到该变量的最新值。

多个线程同时对主内存的一个共享变量进行读取和修改时,首先会读取这个变量到自己的工作内存中成为一个副本,对这个副本进行改动之后,再更新回主内存中变量所在的地方。

(由于CPU时间片是以线程为最小单位,所以这里的工作内存实际上就是指的物理缓存,CPU运算时获取数据的地方;而主内存也就是指的是内存,也就是原始的共享变量存放的位置)

两条规定:

a.线程对共享变量的所有操作必须在工作内存中进行,不能直接操作主内存

b.不同线程间不能访问彼此的工作内存中的变量,线程间变量值的传递都必须经过主内存

如果一个线程1对共享变量x的修改对线程2可见的话,需要经过下列步骤:

a.线程1将更改x后的值更新到主内存

b.主内存将更新后的x的值更新到线程2的工作内存中x的副本

所以,要实现共享变量的可见性必须保证下列两点:

a.线程对工作内存中副本的更改能够及时的更新到主内存上

b.其他线程能够及时的将主内存上共享变量的更新刷新到自己工作内存的该变量的副本上

Java中可以通过synchronized、volatile、java concurrent类来实现共享变量的可见性

但是volatile不能保证变量更改的原子性:

比 如number++,这个操作实际上是三个操作的集合(读取number,number加1,将新的值写回number),volatile只能保证每一 步的操作对所有线程是可见的,但是假如两个线程都需要执行number++,那么这一共6个操作集合,之间是可能会交叉执行的,那么最后导致number 的结果可能会不是所期望的。

所以对于number++这种非原子性操作,推荐用synchronized:

synchronized(this){number++;   
}

如下代码:最后的number的结果不一定是500,有可能是比500小,因为number++不是一个原子性的操作,用volatile不能保证可见性

public class VolatileTest {public static int number = 0;public void increase(){try {Thread.sleep(300);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}number++;}/*** @param args*/public static void main(String[] args) {final VolatileTest test = new VolatileTest();for(int i = 0 ; i < 500 ; i++){new Thread(new Runnable() {@Overridepublic void run() {test.increase();}}).start();}//若当期依然有子线程没有执行完毕while(Thread.activeCount() > 1){Thread.yield();//使得当前线程(主线程)让出CPU时间片}System.out.println("number is " + number);}}

对于自增之类的非原子性操作,只能通过如下方式保证可见性:

a. synchronized

b. ReentrantLock

c. AtomicInteger

synchronized修改如下:

public void increase(){try {Thread.sleep(300);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}synchronized(this){number++;}}

ReentrantLock修改方式如下:

public class VolatileTest {public static int number = 0;public Lock lock =  new ReentrantLock();public void increase(){try {Thread.sleep(300);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}lock.lock();try{number++;//这块的代码实际项目中可能会出现异常,所以要捕获}finally{lock.unlock();//用try finally块保证Unlock一定要执行}}。。。
}

AtomicInteger,一个提供原子操作的Integer的类。在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。而AtomicInteger则通过一种线程安全的加减操作接口。

修改如下:

package com.mooc.test;import java.util.concurrent.atomic.AtomicInteger;public class VolatileTest {public static AtomicInteger number = new AtomicInteger(0);public void increase(){try {Thread.sleep(300);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}number.getAndIncrement();//获得当前值并且加1}/*** @param args*/public static void main(String[] args) {final VolatileTest test = new VolatileTest();for(int i = 0 ; i < 500 ; i++){new Thread(new Runnable() {@Overridepublic void run() {test.increase();}}).start();}//若当期依然有子线程没有执行完毕while(Thread.activeCount() > 1){Thread.yield();//使得当前线程(主线程)让出CPU时间片}System.out.println("number is " + number.get());}}

2. volatile适用情况

a.对变量的写入操作不依赖当前值

比如自增自减、number = number + 5等(不满足)

b.当前volatile变量不依赖于别的volatile变量

比如 volatile_var > volatile_var2这个不等式(不满足)

3. synchronized和volatile比较

a. volatile不需要同步操作,所以效率更高,不会阻塞线程,但是适用情况比较窄

b. volatile读变量相当于加锁(即进入synchronized代码块),而写变量相当于解锁(退出synchronized代码块)

c. synchronized既能保证共享变量可见性,也可以保证锁内操作的原子性;volatile只能保证可见性

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

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

相关文章

如何从零开始开发一个PS5浏览器 | How to develop a PS5 browser

环境&#xff1a;Windows PS5一台 问题&#xff1a;PS5折腾需要使用PKG浏览器访问特定网址&#xff0c;如何自定义网址呢&#xff1f; 解决办法&#xff1a;使用开发套件PS Multi Tools开发一个空应用&#xff0c;利于deeplinkUri 参数访问网页 背景&#xff1a;PS5折腾后&…

dockerpipwork相关测试过程

pipework可以减轻docker实施过程中的工作量&#xff0c;在网上也找了几篇类似的文章&#xff0c;按照相应配置&#xff0c;结果并不相同 如下测试过程记录下&#xff1a; docker run -it --rm --name c1 busybox docker run -it --rm --name c2 busyboxpipework br1 c1 192…

Altium Designer的学习

PCB设计流程 1.新建空白工程&#xff1a; 创建一个新的工程 新建四个文件&#xff0c;并且保存&#xff1a; 每次打开文件时&#xff0c;打开以.PrjPcb结尾的文件 2.元件符号的创建&#xff1a; 在绘制图形的时候设置成10mil,为了在原理图中显得不那么大。 在绘制引脚的时候设…

拦截器,AOP,自定义注解的使用

自定义注解AOP&#xff0c;实现 进入方法打印参数日志 /*** 定义进入方法前打印日志注解* author zy*/ Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) Documented public interface PrintLog {} /*** 定义一个切面&#xff0c;在使用PrintLog注解的方法进…

Hadoop3.x基础(2)- HDFS

来源&#xff1a;B站尚硅谷 目录 HDFS概述HDFS产出背景及定义HDFS优缺点HDFS组成架构HDFS文件块大小&#xff08;面试重点&#xff09; HDFS的Shell操作&#xff08;开发重点&#xff09;基本语法命令大全常用命令实操准备工作上传下载HDFS直接操作 HDFS的API操作HDFS的API案例…

springboot 怎么设置局域网访问

如何配置Spring Boot应用以实现局域网访问 在开发一个Spring Boot应用时&#xff0c;我们通常会通过localhost来访问和测试我们的应用。但是&#xff0c;当我们想要在局域网中分享我们的应用&#xff0c;供其他设备访问时&#xff0c;仅仅使用localhost是不够的。本文将引导你…

Linux系统卸载重装JDK

CentOS 系统是开发者常用的 Linux 操作系统&#xff0c;安装它时会默认安装自带的旧版本的 OpenJDK&#xff0c;但在开发者平时开发 Java 项目时还是需要完整的 JDK&#xff0c;所以我们部署 CentOS 开发环境时&#xff0c;需要先卸载系统自带的 OpenJDK&#xff0c;再重新安装…

汽车销量可视化分析

目录 一.分析的背景、目的、意义 1、背景 2、目的 3、意义 二.数据来源 三.图表分析 1、汽车品牌销量柱状图 2、中国汽车销量柱状图 3、汽车销量前10排行柱状图 4、汽车厂商销量折线图 ​编辑5、汽车销量词云图 6、汽车车型销量 7、汽车价格分布雷达图 8、汽车分…

免费的ChatGPT网站(7个)

还在为找免费的chatGPT网站或者应用而烦恼吗&#xff1f;博主归纳总结了7个国内非常好用&#xff0c;而且免费的chatGPT网站&#xff0c;AI语言大模型&#xff0c;我们都来接触一下吧。 免费&#xff01;免费&#xff01;免费&#xff01;...&#xff0c;建议收藏保存。 1&…

深入浅出HBase:一文理解HBase基础概念(列存储、时间戳、key-value)、架构特点以及适合的使用场景

文章目录 一. HBase 数据模型1. 行存储与列式存储1.1. 行存储1.2. 列存储 2. HBase 数据模型2.1. 模型概览2.2. 列与列族2.3. 时间戳&#xff1a;定义数据版本2.4. HBase的Key-Value 三. HBase架构1. HBase读写流程简述2. HRegionServer内部内部数据流转&#xff1a;HRegion &l…

Web前端入门 - HTML JavaScript Vue

ps&#xff1a;刚开始学习web前端开发&#xff0c;有什么不正确、不标准的内容&#xff0c;欢迎大家指出~ Web简介 90年代初期&#xff0c;Web1.0&#xff0c;静态页面&#xff0c;不和服务器交互&#xff0c;网页三剑客指Dreamweaver、Fireworks、Flash2000年代中期&#xf…

MySQL事务隔离级别

什么是事务? 事务是逻辑上的一组操作&#xff0c;要么都执行&#xff0c;要么都不执行。 事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账1000元&#xff0c;这个转账会涉及到两个关键操作就是&#xff1a;将小明的余额减少1000元&#xff0c;将小红的余额…

如何将Mac连接到以太网?这里有详细步骤

在Wi-Fi成为最流行、最简单的互联网连接方式之前&#xff0c;每台Mac和电脑都使用以太网电缆连接。这是Mac可用端口的标准功能。 如何将Mac连接到以太网 如果你的Mac有以太网端口&#xff0c;则需要以太网电缆&#xff1a; 1、将电缆一端接入互联网端口&#xff08;可以在墙…

正则表达式补充以及sed

正则表达式&#xff1a; 下划线算 在单词里面 解释一下过程&#xff1a; 在第二行hello world当中&#xff0c;hello中的h 与后面第一个h相匹配&#xff0c;所以hello中的ello可以和abcde匹配 在world中&#xff0c;w先匹配h匹配不上&#xff0c;则在看0&#xff0c;r&#…

前端开发基于Qunee绘制网络拓扑图总结-02

1、渲染连线颜色 *关键函数一定要调用&#xff1a;graph.invalidate()* graph.forEach(function(element) {if (element instanceof Q.Edge) {let arr [#549BF1, #AA8A6E, #8F54F1,#5A70BC,#BCBF5C, #BC5A76, #67B4D4,#B4C9EF, #676AD4, #A86EAA,#5CBF7F, #EFB4B4];let inde…

回归预测 | Matlab实现CPO-LSSVM冠豪猪算法优化最小二乘支持向量机多变量回归预测

回归预测 | Matlab实现CPO-LSSVM冠豪猪算法优化最小二乘支持向量机多变量回归预测 目录 回归预测 | Matlab实现CPO-LSSVM冠豪猪算法优化最小二乘支持向量机多变量回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab实现CPO-LSSVM冠豪猪算法优化最小二乘支持向…

使用 Python 进行自然语言处理第 3 部分:使用 Python 进行文本预处理

一、说明 文本预处理涉及许多将文本转换为干净格式的任务&#xff0c;以供进一步处理或与机器学习模型一起使用。预处理文本所需的具体步骤取决于具体数据和您手头的自然语言处理任务。 常见的预处理任务包括&#xff1a; 文本规范化——将文本转换为标准表示形式&#xff0c;…

客户端熔断器基于golang Grpc具体实现(Google SRE客户端熔断器)

目录 前言 一、什么是Google SRE 二、Google SRE 熔断器的工作流程&#xff1a; 三、Google SRE GRPC 代码实现 四、测试用例 大家可以关注个人博客&#xff1a;xingxing – Web Developer from Somewhere 有关后端问题探讨 前言 当某个用户超过资源配额时&#xff0c…

JUC并发编程-各种锁:公平锁,非公平锁、可重入锁、自旋锁、偏向锁、轻量级锁、重量级锁、锁升级顺序、死锁、死锁排查

21. 各种锁的理解 1&#xff09;公平锁&#xff0c;非公平锁 在Java中&#xff0c;锁&#xff08;Lock&#xff09;是一种用于多线程同步的机制。公平锁和非公平锁是两种不同类型的锁。 公平锁&#xff08;Fair Lock&#xff09;是指线程获取锁的顺序与线程请求锁的顺序保持…

能耗在线监测系统在节能管理中的应用

上海安科瑞电气股份有限公司 胡冠楠 咨询家&#xff1a;“Acrelhgn”&#xff0c;了解更多产品资讯 摘要&#xff1a;开展能耗在线监测系统建设&#xff0c;对加强政府部门和企业节能管理中的应用前景&#xff0c;分析系统在能源消费预测分析、能效对标、节能监察、能源精细化…