Java 中 Volatile 关键字

基本概念

补充一下 java 内存模型中的 可见性、原子性和有序性

可见性:

        指的是线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果,另一个线程马上可以看到。比如 :用 volatile 修饰的变量,就会具有可见性。volatile 修饰的变量不允许线程内部缓存和重排序,即直接修改内存。所以对其他线程是可见的。但是这里要注意一个问题,volatile 只能让被他修饰内容具有可见性,但不能保证它有原子性。比如 volatile int  a = 0 ; 之后有一个操作 a++;这个变量 a 具有可见性,但是 a++ 依然是一个非原子操作,也就是这个操作同样存在线程安全问题。

        在 java 中volatilesynchronized final 实现可见性。

原子性:

        原子是程序的最小单位,具有不可分割性。比如 a = 0;这个操作是不可分割的,那么我们就可以称这个操作时原子操作。再比如:a++; 这个操作实际是 a = a + 1;是可分割的,所以它不是一个原子操作。非原子操作都会存在线程安全的问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。一个操作时原子操作,那么我们称它具有原子性。

在java 中 synchronized 和在 lockunlock中操作保证原子性。

有序性:

        Java 提供了 volatilesynchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含 "禁止指令重排序" 的语义,synchronized 是由"一个变量在同一时刻只允许一条线程对其进行 lock 操作" 这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。

1 /**2  * @author zhengbinMac3  */4 public class NoVisibility {5     private static boolean ready;6     private static int number;7     private static class ReaderThread extends Thread {8         @Override9         public void run() {
10             while(!ready) {
11                 Thread.yield();
12             }
13             System.out.println(number);
14         }
15     }
16     public static void main(String[] args) {
17         new ReaderThread().start();
18         number = 42;
19         ready = true;
20     }
21 }

在这段代码中 NoVisibility  可能会持续循环下去,因为读线程可能永远都看不到 ready 值。甚至 NoVisibility 可能会输出 0,因为读线程可能看到了写入 ready 的值,但却没有看到之后写入 number 的值,这种现场被称为 “重排序”。只要在某个线程中无法检测到重排序的情况(即使在其他线程中可以明显地看到该线程中的重排序),那么就无法确保线程中的操作将按照程序中指定的顺序来执行。当主线程首先写入 number,然后再没有同步的情况下写入 ready ,那么读线程看到的顺序可能与写入的顺序完全相反。

在没有同步的情况下,编译器、处理器以及运行时间等都可能对操作的执行顺序进行一些意想不到的调整。在缺乏足够同步的多线程程序中,要想对内存操作的执行顺序进行判断,无法得到正确的结论。

Volatile 原理

Java 语言提供了以中国稍弱的同步机制,即 volatile 变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为 volatile 类型后,编译器运行时就会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile 变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取 volatile 类型的变量时总会返回最新写入的值。

在访问 volatile 变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此 volatile 变量是一种比 sychronized 关键字更轻量级的同步机制

当对非 volatile 变量进行读写的时候,每个线程先从内存拷贝变量到 COU缓存中。如果计算机有多个 CPU,每个线程可能在不同的 CPU上被处理,这意味着每个线程可以拷贝到不同的 CPU cache 中。

        而 声明变量是 volatile 的,JVM 保证了每次变量都从内存中读,跳过了 CPU cache 这一步

当一个变量定义为 volatile 后,将具备两种特性:

  1. 保证此变量对所有的线程的可见性,当一个线程修改了这个变量的值,volatile 保证了新值能够立即同步到主内存,以及每次使用前立即从主内存刷新。
  2. 禁止指令重排序优化。有 volatile 修饰的变量,赋值后多执行了一个 “load addl $0x0,(%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障。

(指令重排序:CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)

Volatile 性能:

        volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要再本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

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

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

相关文章

froeach迭代删除和List迭代删除问题

场景:我有一个 List<ISSLogMessage> records 数据,需要从里面删除指定内容数据 第一次写成 foreach(var item in records) {if (item.logMessage.Contains("上传通行记录"))records.Remove(item); } 直接报错,因为foreach 是个迭代器 直接移除它的对象会报…

Redis实现简易消息队列的三种方式

Redis实现简易消息队列的三种方式 消息队列简介 消息队列是一种用于在计算机系统中传递和处理数据的重要工具。如果你完全不了解消息队列&#xff0c;不用担心&#xff0c;我将尽力以简单明了的方式来解释它。 首先&#xff0c;想象一下你正在玩一个游戏&#xff0c;而游戏中…

Web应用防火墙的性能优化技术

Web应用防火墙&#xff08;WAF&#xff09;是企业网络安全的重要屏障&#xff0c;其性能直接影响到网络服务的质量和安全。本文详细探讨了WAF性能优化的几种技术&#xff0c;旨在为网络安全专业人员提供实用的参考。 规则优化 1.1 精简规则集 规则评估&#xff1a;定期评估规…

JQuery、JSON、AJAX、XML、IO流、多线程、反射核心知识点详解

JQuery 一、什么是JQuery JQuery是JavaScript的一个框架&#xff0c;对js的封装&#xff0c;使得js简单易学 优点&#xff1a; 1、不用考虑浏览器兼容性问题 2、jquery拥有强大的选择器&#xff0c;简化了js代码 3、jquery提供了很多系统函数&#xff0c;直接调用 二、版本 1.x…

面试题补充

1.公司有几套环境&#xff1a;测试环境&#xff08;测试人员使用&#xff09;&#xff0c;开发环境&#xff08;开发人员使用&#xff09;&#xff0c;预生产环境&#xff08;测试人员使用&#xff09;&#xff0c;生产环境&#xff08;用户使用&#xff09; 2.作为一名测试&a…

C语言第一节--从c语言源码到可执行文件

C语言第一节–从c语言源码到可执行文件 编译 C 语言代码生成可执行文件的流程一般可以分为四个步骤&#xff1a;预处理、编译、汇编和链接。 假设有一个名为 “hello.c” 的 C 语言源文件&#xff0c;内容如下&#xff1a; #include <stdio.h>int main() {printf("…

Kubernetes 常用命令 持续更新

1、进入指定namespace pod kubectl exec -it --namespacekube-system g-lsb-proxy-nginx-r7zfl-2522744936-11rld /bin/sh kubectl exec -it g-lsb-proxy-nginx-r7zfl-2522744936-9tz5k -n kube-system /bin/bash2、查看k8s pod详情 kubectl describe pods -n jiankunking …

【计算机网络】网络编程接口 Socket API 解读(11)

Socket 是网络协议栈暴露给编程人员的 API&#xff0c;相比复杂的计算机网络协议&#xff0c;API 对关键操作和配置数据进行了抽象&#xff0c;简化了程序编程。 本文讲述的 socket 内容源自 Linux man。本文主要对各 API 进行详细介绍&#xff0c;从而更好的理解 socket 编程。…

opencv跨平台arm交叉编译之ubuntu

目录 1. 安装交叉编译工具链2. 安装依赖3. 配置工具链3.1 新建build目录3.2 安装cmake-gui3.3 工具链配置界面进行配置3.3.1 终端输入以下命令3.3.2 点击Configure&#xff0c;弹出编译方式选择对话框&#xff1a;3.3.3 点击Next3.3.4 点击Finish3.3.5 点击Configure。3.3.6 Ge…

RISC-V 特权级架构

特权级别 级别的数值越大&#xff0c;特权级越高&#xff0c;掌控硬件的能力越强&#xff0c;在CPU硬件层面&#xff0c;M模式必须存在&#xff0c;其它模式可以不存在 执行环境调用 ecall &#xff0c;这是一种很特殊的陷入类的指令&#xff0c; 相邻两特权级软件之间的接口正…

2023年9月Web3行业月度发展报告区块链篇 | 陀螺科技会员专享

9月是加密市场的活动月&#xff0c;斯坦福区块链周、Token2049等大型活动相继举办&#xff0c;后者更是创下超过1万人的历史最高纪录&#xff0c;成为了全球最大的Web3活动。在本次Token2049上&#xff0c;RWA、支付以及出入金成为了讨论度最多的活动。尽管活动如火如荼&#x…

docker入门加实战—docker数据卷

docker入门加实战—docker数据卷 容器是隔离环境&#xff0c;容器内程序的文件、配置等都在容器的内部&#xff0c;要读写容器内的文件非常不方便。 因此&#xff0c;容器提供程序的运行环境&#xff0c;但是程序运行产生的数据、程序运行依赖的配置都应该与容器进行解耦。 …

JUC第二十八讲:JUC工具类: Semaphore详解

JUC工具类: Semaphore详解 本文是JUC第二十八讲&#xff0c;JUC工具类: Semaphore详解。Semaphore底层是基于AbstractQueuedSynchronizer来实现的。Semaphore称为计数信号量&#xff0c;它允许n个任务同时访问某个资源&#xff0c;可以将信号量看做是在向外分发使用资源的许可证…

亳州市的自然风光与旅游资源:欣赏安徽省中部的壮丽景色

亳州市是中国安徽省的一个地级市&#xff0c;位于该省的中部。 亳州市辖区包括谯城区、涡阳县、蒙城县和利辛县等地。亳州市拥有悠久的历史和丰富的文化遗产&#xff0c;同时也以其独特的自然风光而闻名。 首先&#xff0c;让我们来了解一下亳州的历史和景点。亳州的历史可以…

软件‘小程序‘前台开发软件定制的知识|app网站搭建

软件&#xff0c;小程序&#xff0c;前台开发软件定制的知识 随着互联网的快速发展&#xff0c;软件&#xff0c;小程序&#xff0c;前台开发软件定制已经成为了企业必备的工具。它可以帮助企业更好地管理业务&#xff0c;提高效率&#xff0c;增强用户体验。那么&#xff0c;什…

vue3学习(五)--- 父子组件传值

文章目录 defineProps普通写法TS写法 defineEmits普通写法TS写法 defineExpose defineProps 和 defineEmits 都是只能在 <script setup> 中使用的编译器宏。他们不需要导入&#xff0c;且会随着 <script setup> 的处理过程一同被编译掉。 defineProps 接收父组件传…

MySQL中使用函数会使索引失效?

文章目录 1、前置准备2、ChatGPT的答案3、实践证明SQL1SQL2SQL3SQL4SQL5 4、总结 1、前置准备 首先创建我们要测试的库表 CREATE TABLE lianhe_index (id int(11) NOT NULL AUTO_INCREMENT COMMENT id,name varchar(255) DEFAULT NULL,age int(11) DEFAULT NULL,number int(1…

Flink报错could not be loaded due to a linkage failure

文章目录 1、报错2、原因3、解决 1、报错 在Flink上提交作业&#xff0c;点Submit没反应&#xff0c;F12看到接口报错信息为&#xff1a; 大概意思是&#xff0c;由于链接失败&#xff0c;无法加载程序的入口点类xx。没啥鸟用的信息&#xff0c;去日志目录继续分析&#xff1a…

Django Channels、WS协议及同步与异步详解

CONTENTS 1. 同步与异步2. WebSocket3. 在JavaScript中使用WebSocket4. Django Channels 1. 同步与异步 在 Django 中&#xff0c;同步和异步主要涉及到请求处理的方式。这两种方式的主要区别在于它们如何处理多个并发请求&#xff1a; 同步&#xff08;Synchronous&#xff…

删除的通话记录也能找回!如何iPhone很早以前的通话记录

是否搜索过忘记保存的拨号号码?是的!我们都经历过,这可能真的很烦人,尤其是当你的iPhone决定隐藏你的通话记录时。但别担心,在这篇文章中,我将向你展示如何使用各种方法查看一个月前iPhone上的通话历史记录。因此,让我们来了解和发现如何在iPhone上查看你过去的通话记录…