并发编程volatile精解

多线程下变量的不可见性

在多线程并发执行的情况下,多个线程修改共享的成员变量,会出现一个线程修改了共享变量的值后,另一个线程不能直接看到该线程修改后的变量最新值。(多线程下修改共享变量会出现变量修改值后的不可见性)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可见性问题的原因
所有共享变量存在于主内存中,每个线程由自己的本地内存,而且线程读写共享数据也是通过本地内存交换的,所以才导致了可见性问题。
解决方法
1、加锁 2、使用volatile关键字(底层实现原理是内存屏障)【并发编程】volatile
在这里插入图片描述
在这里插入图片描述
volatile修改的变量可以在多线程并发修改下,实现线程间变量的可见性
在这里插入图片描述

在这里插入图片描述
在Java中,volatile 关键字确实不保证复合操作的原子性,但是对于long和double类型的变量,volatile 修饰的读写操作是原子的。
在这里插入图片描述
在多线程环境中,vulatile关键字可以保证共享数据的可见性,但是不能保证对数据操作的原子性(在多线程环境下volatile修饰的变量也是不安全的)
要保证数据的安全性,还需要使用锁机制。
在这里插入图片描述
什么是重排序
为了提高性能,编译器和处理器常常会对既定的代码执行顺序进行指令重排序。
在这里插入图片描述

好处
重排序可以提高处理的速度。
在这里插入图片描述
使用volatile可以禁止指令的重排序,从而修正重排序可能带来的并发安全问题。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(7)线程中断规则:对线程 interrupt 方法的调用,happens-before 被中断线程的代码检测到中断事件的发生。
(8)对象终结规则:一个对象的初始化完成,happens-before 它的 finalize() 方法的开始。

在这里插入图片描述

long和double的原子性

在java中,long和double都是8个字节共64位(一个字节=8bit),那么如果是一个32位的系统,读写long或double的 变量时会涉及到原子性问题,因为32位的系统要读完一个64位的变量,需要分两步执行,每次读取32位,这样就对double和long变量的赋值就会出现问题: 如果有两个线程同时写一个变量内存,一个进程写低32位,而另一个写高32位,这样将导致获取的64位数据是失效的数据。

public class LongTest09 implements Runnable{ private volatile static long aLong = 0; private volatile long value;public LongTest09(long value) { this.setValue(value);}public long getValue() { return value;}public void setValue(long value) { this.value = value;}@Overridepublic void run() { int i = 0;while (i < 100000) {LongTest09.aLong = this.getValue(); i++;// 赋值操作!!long temp = LongTest09.aLong; // 取出值操作!!!if (temp != 1L && temp != -1L) {System.out.println("出现错误结果" + temp); System.exit(0);} }System.out.println("运行正确"); }public static void main(String[] args) throws InterruptedException { // 获取并打印当前JVM是32位还是64位的String sysNum = System.getProperty("sun.arch.data.model"); System.out.println("系统的位数:"+sysNum);LongTest09 t1 = new LongTest09(1); LongTest09 t2 = new LongTest09(-1); Thread T1 = new Thread(t1);Thread T2 = new Thread(t2); T1.start();T2.start(); T1.join(); T2.join(); }
}

上面的代码在32位环境和64位环境执行的结果是不一样的: 32位环境:出现错误结果 原因:32位环境无法一次读取long类型数据,多线程环境下对Long变量的读写是不完整的,导致temp变量既不等于1也不等于-1。出现了long和double读写的原子性问题了。 64位环境:运行正确
如果是在64位的系统中,那么对64位的long和double的读写都是原子操作的。即可以以一次性读写long或double的整个64bit。如果在32位的JVM上,long和double就不是原子性操作了。

解决办法

  1. 对于64位的long和double,如果没有被volatile修饰,那么对其操作可以不是原子的。在操作的时候,可以分成两步,每次对32位操作。
  2. 如果使用volatile修饰long和double,那么其读写都是原子操作
  3. 在实现JVM时,可以自由选择是否把读写long和double作为原子操作;
  4. java中对于long和double类型的写操作不是原子操作,而是分成了两个32位的写操作。读操作是否也分成了两 个32位的读呢?在JSR-133之前的规范中,读也是分成了两个32位的读,但是从JSR-133规范开始,即JDK5开始,读操作也都具有原子性;
  5. java中对于其他类型的读写操作都是原子操作(除long和double类型以外);
  6. 对于引用类型的读写操作都是原子操作,无论引用类型的实际类型是32位的值还是64位的值;
  7. Java商业虚拟机已经解决了long和double的读写操作的原子性。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    双重检查锁
    双重检查锁(double checked locking)是对上述问题的一种优化。先判断对象是否已经被初始化,再决定要不要加锁。
public class Singleton {
//静态属性,volatile保证可见性和禁止指令的重排序private  volatile static Singleton uniqueSingleton;private Singleton() {}public Singleton getInstance() {//第一重检查锁定if (null == uniqueSingleton) {//同步代码块synchronized (Singleton.class) {//第二重检查锁定if (null == uniqueSingleton) {//注意:非原子操作uniqueSingleton = new Singleton();   // error}}}return uniqueSingleton;}
}
  • 检查变量是否被初始化(不去获得锁),如果已被初始化则立即返回。
  • 获取锁。
  • 再次检查变量是否已经被初始化,如果还没被初始化就初始化一个对象。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public class UseVolatile1 implements Runnable { volatile boolean flag = false;AtomicInteger realA = new AtomicInteger(); @Overridepublic void run() {for (int i = 0; i < 10000; i++) { setDone();realA.incrementAndGet(); }}private void setDone() {flag = true;   // 纯赋值操作符合预期 // flag = !flag ; // 这样做不符合预期 }
}
class Test{public static void main(String[] args) throws InterruptedException { UseVolatile1 r =  new UseVolatile1();Thread thread1 = new Thread(r); Thread thread2 = new Thread(r);thread1.start();thread2.start();thread1.join();thread2.join();System.out.println(r.flag);System.out.println(r.realA.get()); }
}

在这里插入图片描述
触发器
按照volatile的可见性和禁止重排序以及happens-before规则,volatile可以作为刷新之前变量的触发器。我们可以将某个变量设置为volatile修饰,其他线程一旦发现该变量修改的值后,触发获取到的该变量之前的操作都将是最新的且可见。

public class VisibilityHP {int a = 1;int b = 2;int c = 3;volatile boolean flag = false; private void write() {a = 3;b = 4 ;c = a;flag = true; }private void read() {// flag被volatile修饰,充当了触发器,一旦值为true,此处立即对变量之前的操作可见。 while(flag){System.out.println("a=" + a + ";b=" + b +",c="+c ); }}public static void main(String[] args) { while (true) {VisibilityHP test = new VisibilityHP(); new Thread(new Runnable() {@Overridepublic void run() { try {Thread.sleep(100);} catch (InterruptedException e) { e.printStackTrace();}test.write(); }}).start();new Thread(new Runnable() { @Overridepublic void run() { try {Thread.sleep(100);} catch (InterruptedException e) { e.printStackTrace();}test.read(); }}).start();}}}

volatile可以作为刷新之前变量的触发器。我们可以将某个变量设置为volatile修饰,其他线程一旦发现该变量修改的 值后,触发获取到的该变量之前的操作都将是最新的且可见。

volatile与synchronized

在这里插入图片描述

volatile总结

在这里插入图片描述

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

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

相关文章

十款外贸软件盘点,专注企业订单业务管理

在当今全球化的市场环境中&#xff0c;外贸企业的发展面临着诸多挑战与机遇。如何高效管理企业业务&#xff0c;提升运营效率&#xff0c;成为外贸企业在激烈竞争中脱颖而出的关键。外贸业务管理ERP软件作为一种强大的工具&#xff0c;能够整合企业资源、优化管理流程、实现数据…

yaml文件编写

Kubernetes 支持YAML和JSON格式管理资源 JSON 格式:主要用于 api 接口之间消息的传递 YAML 格式;用于配置和管理,YAML是一种简洁的非标记性语言,内容格式人性化容易读懂 一&#xff0c;yaml语法格式 1.1 基本语法规则 使用空格进行缩进&#xff08;不使用制表符&#xff0…

Node.js 全栈开发进阶篇

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;node.js篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来node.js篇专栏内容:node.js- 全栈开发进阶篇 前言 大家好&#xff0c;我是青山。在上一篇文章中&#xff0c;…

密码学与数学证明。

通常的数学证明工具主要有&#xff1a;以平行公设为基础的几何证明体系&#xff0c;以牛顿三定律为基础的经典力学证明体系&#xff0c;以四则计算为基础的计算证明方法&#xff0c;以正则三段论为基础的断言思证系统&#xff0c;以数学归纳法和夹挤定理为基础的包络收敛分析证…

Vue(JavaScript)读取csv表格并求某一列之和(大浮点数处理: decimal.js)

文章目录 想要读这个表格&#xff0c;并且求第二列所有价格的和方法一&#xff1a;通过添加文件输入元素上传csv完整&#xff08;正确&#xff09;代码之前的错误部分因为价格是小数&#xff0c;所以下面的代码出错。如果把parseFloat改成parseInt&#xff0c;那么求和没有意义…

C语言初阶必会的练习题(3)之位操作符(^ 、、>>等)的应用

C语言初阶必会的练习题&#xff08;3&#xff09; 放在最前面的1、不允许创建临时变量&#xff0c;交换两个整数的内容1.1、分析&#xff1a;见代码注释&#xff08;a&#xff09;方法 1&#xff08;b&#xff09;方法 2 1.2、结果展示方法 1 的 结果&#xff1a;方法 2 的 结果…

基于SSM框架的乡村农户对口扶贫系统

基于SSM框架的乡村农户对口扶贫系统。 设计步骤&#xff1a; 项目架构创建&#xff1a;首先创建项目的基本架构&#xff0c;包括com.zc.xxx路径下的文件和resources资源文件夹。 SSM架构&#xff1a;使用Spring、SpringMVC、MyBatis作为后端架构&#xff0c;采用POJO—Dao—…

前端Web用户 token 持久化

用户 token 持久化 业务背景&#xff1a;Token的有效期会持续一段时间&#xff0c;在这段时间内没有必要重复请求token&#xff0c;但是pinia本身是基于内存的管理方式&#xff0c;刷新浏览器Token会丢失&#xff0c;为了避免丢失需要配置持久化进行缓存 基础思路&#xff1a…

【学习笔记】SAP ABAP——OPEN SQL(一)【INTO语句】

【INTO语句】 结构体插入(插入一条语句时) SELECT...INTO [CORRESPONDING FIELDS OF] <wa> FROM <db> WHERE <condition>.内表插入(插入多条语句时) SELECT...INTO|APPENDING [CORRESPONDING FIELDS OF] TABLE <itab>FROM <db> WHERE <con…

微服务透传日志traceId

问题 在微服务架构中&#xff0c;一次业务执行完可能需要跨多个服务&#xff0c;这个时候&#xff0c;我们想看到业务完整的日志信息&#xff0c;就要从各个服务中获取&#xff0c;即便是使用了ELK把日志收集到一起&#xff0c;但如果不做处理&#xff0c;也是无法完整把一次业…

Qt中实现高准确率的语音识别

选择语音识别引擎 开源语音识别项目中&#xff0c;以下两款工具可以用于支持中英文识别&#xff0c;并且与Qt兼容&#xff1a; Vosk&#xff1a;Vosk是一个开源的语音识别工具&#xff0c;支持中英文及多种语言&#xff0c;具备离线识别能力&#xff0c;且不依赖互联网。 Padd…

c语言中的线程管理pthread详解

在C语言中,多线程编程常用的POSIX线程(POSIX Threads, pthreads)库主要由pthread.h头文件提供。pthread.h定义了许多用于线程创建、管理、同步的函数和数据结构。下面是pthread.h中的核心概念和主要函数的详细介绍。 1. 基本概念 线程:线程是一个轻量级的进程,可以并发执…

十五:java web(7)-- Spring Boot

目录 1. Spring Boot 简介 1.1 简介 1.2 Spring Boot 的特点 1.3 Spring Boot 和 Spring 的关系 2. Spring Boot 快速入门 2.1 创建第一个 Spring Boot 项目 3. Spring Boot 配置管理 3.1 application.properties 和 application.yml 配置 这两种都可以 好像现在更推荐…

关于打开网页非常慢的解决方法

方法一&#xff1a;刷新dns缓存 ipconfig /flushdns方法二&#xff1a;许多网站&#xff0c;太落后&#xff0c;不支持ipv6&#xff0c;所以关闭ipv6即可

使用Python简单实现客户端界面

服务端实现 import threading import timeimport wx from socket import socket, AF_INET, SOCK_STREAMclass LServer(wx.Frame):def __init__(self):wx.Frame.__init__(self, None, id1002, titleL服务器端界面, poswx.DefaultPosition, size(400, 450))# 窗口中添加面板pl …

JDK1.5 java代码打包jar HmacSha256

文章目录 demo地址背景实现编写代码编译class文件打包 JAR 文件执行生成的 JAR 文件辅助验证方式 常见问题和解决方法常规生成jar方案maven插件idea工具 demo地址 https://github.com/xiangge-zx/HmacSha256 背景 最近接到一个需求,做一个可以用来HmacSha256加密的小工具&am…

ubuntu 22.04 server 安装 xtrabackup 2.4 qpress LTS

ubuntu 22.04 server 安装 xtrabackup 2.4 qpress LTS 参考 https://docs.percona.com/percona-xtrabackup/innovation-release/apt-repo.html https://www.ubuntuupdates.org/ppa/percona_server_with_xtradb?distjammy centos7 xtrabackup mysql&#xff08;8&#xff0…

MySQL 8.0中的‘caching_sha2_password‘身份验证插件问题

使用Navicat Premium 12连接MySQL数据库时会出现Authentication plugin ‘caching_sha2_password’ cannot be loaded的错误。 出现这个原因是mysql8 之前的版本中加密规则是mysql_native_password,而在mysql8之后,加密规则是caching_sha2_password, 解决问题方法有两种,一种是…

Go八股(Ⅳ)***slice,string,defer***

***slice&#xff0c;string&#xff0c;defer*** 1.slice和arry的区别 arry&#xff1a; Go语言中arry即为数据的一种集合&#xff0c;需要在声明时指定容量和初值&#xff0c;且一旦声明就长度固定&#xff0c;访问时按照索引访问。通过内置函数len可以获取数组中的元素个…

雪花算法(Snowflake Algorithm)C# 实现版本

这里采用10位的工作ID,当时间回拔时,采用工作ID增加1来避免生成与旧的ID重复。 雪花算法的结构 雪花算法生成的ID是一个64位的Long型数字,结构如下: 高41位:时间戳(timestamp)中间10位:工作节点ID(workerId),用来避免时间回拔。低12位:序列号(sequence)时间戳(…