Java 中 Volatile 关键字

news2025/2/26 19:36:55

基本概念

补充一下 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 zhengbinMac
 3  */
 4 public class NoVisibility {
 5     private static boolean ready;
 6     private static int number;
 7     private static class ReaderThread extends Thread {
 8         @Override
 9         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.coloradmin.cn/o/1087554.html

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

相关文章

程序员自由创业周记#13:第一桶金

国庆假期 对于我而言是没有放假的概念的,可以说每天都是假期,但是孩子放假就不能做软件了,得陪着他,尤其他生了半个月的病,隔三差五就得去医院排两小时队看个医生,周记因此耽搁了两次。没有看到我的更新不…

【Java每日一题】——第二十九题:超市购物程序设计(2023.10.13)

🎃个人专栏: 🐬 算法设计与分析:算法设计与分析_IT闫的博客-CSDN博客 🐳Java基础:Java基础_IT闫的博客-CSDN博客 🐋c语言:c语言_IT闫的博客-CSDN博客 🐟MySQL&#xff1a…

二十一、动态内存管理

1 动态内存开辟 目前为止,我们已经掌握的内存开辟方式有: int val 20;//在栈空间上开辟四个字节 char arr[10] {0};//在栈空间上开辟10个字节的连续空间上述开辟空间的方式其实有两个缺点: 空间开辟的大小是固定的,一旦开辟就…

go语言基础之变量

目录 视频学习地址:Go零基础入门_在线视频教程-CSDN程序员研修院 一. 单变量声明和赋值 1、变量的声明 2、变量赋值 3、声明并赋值 二. 多变量声明和赋值 1、多变量声明 2、多变量赋值 三. 变量声明赋值的简易写法 1、单变量简易写法 2、多变量简易写法 …

14. SpringBoot项目之数据保存到数据库

SpringBoot项目之数据保存到数据库 1. 创建数据库&表 本栏目前面文章中已经讲过MySql连接SqlYog以及创建数据库&表 —>传送门 2. 导入ORM依赖&配置 在pom.xml中导入 要更新maven环境 <dependency><groupId>org.mybatis.spring.boot</groupI…

十个面试排序算法

一、 前言 最常考的是快速排序和归并排序&#xff0c;并且经常有面试官要求现场写出这两种排序的代码。对这两种排序的代码一定要信手拈来才行。还有插入排序、冒泡排序、堆排序、基数排序、桶排序等。面试官对于这些排序可能会要求比较各自的优劣、各种算法的思想及其使用场景…

Python爬虫(二十三)_selenium案例:动态模拟页面点击

本篇主要介绍使用selenium模拟点击下一页&#xff0c;更多内容请参考:Python学习指南 #-*- coding:utf-8 -*-import unittest from selenium import webdriver from selenium.webdriver.common.keys import Keys from bs4 import BeautifulSoup import timeclass douyuSelenium…

hive 之select 中文乱码

此处的中文乱码和mysql的库表 编码 latin utf 无关。 直接上案例。 有时候我们需要自定义一列&#xff0c;有时是汉字有时是字母&#xff0c;结果遇到这种情况了。 说实话看到这真是糟心。这谁受得了。 单独select 没有任何问题。 这是怎么回事呢&#xff1f; 经过一番检查&…

记录遇到的前端面试题,欢迎指正

css 1.隐藏元素的方式 转自将页面元素隐藏的10种方法 display:none display属性用于设置页面元素的显示方式&#xff0c;能够控制元素的显示或者隐藏&#xff0c;当他的值被设置为none时&#xff0c;就会隐藏对应的元素&#xff0c;使其不可见。 这种方法是最常用的隐藏元素的…

uboot启动流程-board_init_r函数执行过程

一. uboot启动流程 本文来了解 board_init_r 函数执行过程。_main函数会调用到 board_init_r 函数。 二. board_init_r函数执行过程 _main 函数会调用到 board_init_r 函数。 _main 函数在 uboot的 /arch/arm/lib/crt0.S 文件中。_main函数中&#xff0c;执行完 relocate_…

SLAM从入门到精通(ROS网络通信)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 之前我们学习ros的时候&#xff0c;大部分都是基于仿真来做的。但是最终&#xff0c;我们还是要把ros部署到小车上的。这就带来一个问题&#xff0…

qgis c++ api 整体框架详解

文章目录 整体架构QGis库官方文档编译生成的库 core地图和图层矢量图层(Vector layers)图层要素符号图层要素要素渲染(feature renderer)符号(symbol) 坐标映射数据源(data provider) Raster layers图层符号数据源坐标映射 core库其他有用类 guiQgsMapCanvasQgsMapToolQgsLayer…

VIT(Vision Transformer)学习(三)-纯VIT之swin transformer模型理解

语义分割网络纯 TRF 结构&#xff1a;VIT主干作为编码器&#xff0c;设计一个基于 TRF 架构的解码器。 今天学习swin transformer 源码地址: https://github.com/microsoft/Swin-Transform 哔哩哔哩讲解&#xff1a;12.1 Swin-Transformer网络结构详解_哔哩哔哩_bilibili 博…

支付宝开放平台第三方代小程序开发,消息服务推送通知总结

大家好&#xff0c;我是小悟 关于支付宝开放平台第三方代小程序开发的消息服务推送通知&#xff0c;是开放平台代小程序实现业务的重要功能。 消息服务推送通知&#xff0c;支持商家两种通讯协议来接收消息&#xff0c;分别为websocket 长连接和http。 关于websocket方式&am…

CentOS 7 编译安装Boost

1、前提条件 linux平台/CentOS 7 下要编译安装Boost除gcc和gcc-c之外&#xff0c;还需要两个开发库&#xff1a;bzip2-devel 和python-devel &#xff0c;因此在安装前应该先保证这两个库已经安装。 安装指令: yum install bzip2 bzip2-devel bzip2-libs python-devel Cent…

学习开发一个RISC-V上的操作系统(汪辰老师) — 01-helloRVOS程序讲解

前言 &#xff08;1&#xff09;此系列文章是跟着汪辰老师的RISC-V课程所记录的学习笔记。 &#xff08;2&#xff09;该课程相关代码gitee链接&#xff1b; &#xff08;3&#xff09;PLCT实验室实习生长期招聘&#xff1a;招聘信息链接 &#xff08;4&#xff09; start.S &a…

SpringBoot项目入门: IDEA 创建SpringBoot项目

方式1:在线创建项目 https://start.spring.io/ 环境准备 &#xff08;1&#xff09;JDK 环境必须是 1.8 及以上&#xff0c;传送门&#xff1a;jdk1.8.191 下载&#xff08;2&#xff09;后面要使用到 Maven 管理工具 3.2.5 及以上版本&#xff08;3&#xff09;开发工具建议…

leetcode-279. 完全平方数

1. 题目链接 链接: 题目链接 2. 解答 #include <stdio.h> #include <stdlib.h> #include <stdbool.h>bool issquare(int n) {if (n 1 || n 4) return true;if (n 2 || n 3) return false;for (int i 3; i < n/2; i ) {if (n i*i) return true;}…

【HCIE】12.VXLAN

VXLAN&#xff0c;是一种IP VPN&#xff0c;每台设备几乎都支持IP&#xff0c;而MPLS会有很多设备不会支持&#xff0c;所以VXLAN是使用的较多。 采用MAC in UDP封装方式&#xff0c;MAC指的是原始数据的以太头部被封装到新的UDP头部里面。是基于三层的一个虚拟网络。 技术背…

13.SpringBoot项目之Service层

SpringBoot项目之Service层 JavaEE三层架构 为了项目维护方便&#xff0c;为了项目开发便利。三层架构功能控制器层&#xff1a;controller方便和前端数据进行交互业务层&#xff1a;service处理各种业务持久化层&#xff1a;mapper和数据库进行数据交互 抽取出service层 按…