当前位置: 首页 > news >正文

【Java 基础篇】Java线程:volatile关键字与原子操作详解

在这里插入图片描述

在多线程编程中,确保线程之间的可见性和数据一致性是非常重要的。Java中提供了volatile关键字和原子操作机制,用于解决这些问题。本文将深入讨论volatile关键字和原子操作的用法,以及它们在多线程编程中的重要性和注意事项。

volatile关键字的作用

volatile关键字用于声明一个变量是"易失性"的,这意味着该变量的值可能会被多个线程同时访问和修改。volatile关键字的主要作用有两个:

  1. 保证可见性volatile关键字确保一个线程对volatile变量的修改对其他线程是可见的。当一个线程修改了volatile变量的值,这个变化会立即被其他线程看到,从而避免了数据不一致的问题。

  2. 禁止指令重排序volatile关键字还可以防止编译器和处理器对被声明为volatile的变量进行重排序优化。这确保了代码的执行顺序与程序员所写的顺序一致,避免了潜在的问题。

volatile关键字的使用示例

让我们通过一个示例来演示volatile关键字的使用:

public class VolatileExample {private volatile boolean flag = false;public void toggleFlag() {flag = !flag;}public boolean isFlag() {return flag;}public static void main(String[] args) {VolatileExample example = new VolatileExample();Thread writerThread = new Thread(() -> {example.toggleFlag();System.out.println("Flag set to true");});Thread readerThread = new Thread(() -> {while (!example.isFlag()) {// 等待flag变为true}System.out.println("Flag is true");});writerThread.start();readerThread.start();}
}

在上述示例中,我们创建了一个VolatileExample类,其中包含一个volatile变量flagwriterThread线程会不断地切换flag的值,而readerThread线程会等待flag变为true后输出相应的信息。由于flagvolatile的,readerThread能够立即看到flag的修改,从而正确地输出信息。

volatile关键字的使用详解

volatile关键字在多线程编程中是一个非常重要的关键字,它可以用来声明一个变量,以确保在多个线程之间的可见性和顺序性。在本节中,我们将详细讨论volatile关键字的使用,包括何时使用它以及如何正确使用它。

何时使用volatile

1. 状态标志

volatile关键字常用于状态标志的管理,例如在多线程中控制线程的启停。通过将状态标志声明为volatile,可以确保一个线程对状态标志的修改对其他线程是可见的。

public class StoppableTask extends Thread {private volatile boolean stopped = false;public void run() {while (!stopped) {// 执行任务}}public void stopTask() {stopped = true;}
}

在上面的示例中,stopped标志用于控制线程的执行。通过将stopped声明为volatile,确保了stopTask方法修改标志后,线程立即看到标志的变化,从而安全地停止线程的执行。

2. 单次初始化

volatile还可以用于实现一种延迟初始化的模式,确保对象只被初始化一次。

public class LazyInitialization {private volatile ExpensiveObject instance;public ExpensiveObject getInstance() {if (instance == null) {synchronized (this) {if (instance == null) {instance = new ExpensiveObject();}}}return instance;}
}

在上述示例中,getInstance方法使用了双重检查锁定以确保instance只被初始化一次。同时,由于instance被声明为volatile,可以确保初始化的状态对其他线程是可见的。

注意事项

使用volatile关键字需要特别注意一些注意事项:

  1. 不适用于复合操作volatile关键字适用于单一变量的读写操作,但不适用于复合操作,例如递增操作,因为递增操作不是一个原子操作。

  2. 不保证原子性volatile关键字可以保证可见性,但不能保证原子性。如果需要执行一系列操作并保证原子性,需要考虑使用锁或原子操作类。

  3. 不替代锁volatile关键字和锁机制各有各的应用场景,不能替代彼此。锁机制适用于复杂的临界区操作,而volatile更适用于简单的状态标志管理和单次初始化。

  4. 性能开销较低:相对于锁机制,volatile关键字的性能开销较低,因此在某些情况下更为适用。

原子操作的使用详解

原子操作是多线程编程中的重要概念,它用于确保某些操作是不可分割的,从而避免竞态条件和数据不一致性问题。在Java中,可以通过java.util.concurrent包中的原子类来实现原子操作。本节将详细介绍原子操作的使用,包括何时使用原子操作以及如何使用原子类。

何时使用原子操作

原子操作适用于以下情况:

  1. 递增或递减操作:当多个线程需要对一个变量进行递增或递减操作时,使用原子操作可以避免竞态条件,确保操作的原子性。

  2. 检查并更新操作:在某些情况下,需要检查一个变量的值,然后根据检查结果来更新变量。原子操作可以确保检查和更新是一个不可分割的操作。

  3. 计数器操作:原子操作特别适用于计数器的增加和减少操作,例如线程安全的计数器。

  4. 状态标志操作:如果需要在多个线程之间共享状态标志,并进行安全的检查和修改,原子操作是一种可行的选择。

使用原子类

Java提供了一系列原子类,位于java.util.concurrent.atomic包中,用于支持原子操作。常用的原子类包括AtomicIntegerAtomicLongAtomicBoolean等,它们分别用于整数、长整数和布尔值的原子操作。

1. 原子递增和递减
import java.util.concurrent.atomic.AtomicInteger;public class AtomicCounter {private AtomicInteger count = new AtomicInteger(0);public int increment() {return count.incrementAndGet();}public int decrement() {return count.decrementAndGet();}public int getCount() {return count.get();}
}

在上述示例中,AtomicInteger用于实现线程安全的计数器。incrementAndGetdecrementAndGet方法分别用于原子递增和递减操作。

2. 原子检查并更新
import java.util.concurrent.atomic.AtomicReference;public class AtomicConfig {private AtomicReference<String> config = new AtomicReference<>("default");public void updateConfig(String newConfig) {config.set(newConfig);}public boolean isConfigUpdated(String expectedConfig) {return config.compareAndSet(expectedConfig, "newConfig");}public String getConfig() {return config.get();}
}

在上述示例中,AtomicReference用于实现原子检查并更新操作。compareAndSet方法用于检查当前值是否与期望值相同,如果相同则更新为新值。

3. 其他原子操作

除了上述示例中的原子递增、递减和检查并更新操作,原子类还提供了其他常用的原子操作,如原子赋值、原子加法、原子减法等。

注意事项

使用原子操作时需要注意以下事项:

  1. 性能开销较高:原子操作通常比普通的非原子操作具有更高的性能开销,因此应仅在必要时使用。

  2. 不适用于复合操作:原子操作适用于单一变量的原子操作,不适用于复合操作。对于复合操作,可以使用锁机制或其他同步方式。

  3. 不替代锁:原子操作和锁机制各有各的应用场景,不能替代彼此。锁机制适用于复杂的临界区操作,而原子操作更适用于简单的原子性操作。

  4. 线程安全性:原子操作确保了单个操作的原子性,但不一定能够保证多个操作的线程安全性,因此在实际使用中需要综合考虑线程安全性。

原子操作与volatile关键字的区别

虽然volatile关键字可以确保可见性和禁止指令重排序,但它并不能保证原子性。原子操作是指不可分割的操作,而volatile只能保证单个操作的可见性。如果需要执行一系列操作并保证其原子性,需要使用原子操作类,如AtomicIntegerAtomicLong等,或者使用锁机制。

原子操作的重要性

原子操作是多线程编程中的关键概念之一,它们可以确保多个线程在访问共享资源时不会产生竞态条件和数据竞争。Java提供了一系列原子操作类,如AtomicIntegerAtomicLongAtomicReference等,它们提供了一些常见的原子操作方法,如递增、递减、比较并交换等。

使用原子操作可以提高程序的性能和可靠性,避免了锁机制带来的性能开销和死锁等问题。在多线程编程中,合理地使用volatile关键字和原子操作是确保线程安全的关键步骤。

注意事项

在使用volatile关键字时,需要注意以下几点:

  1. volatile适用于单一变量的读写操作,如果涉及到多个变量之间的操作,需要考虑使用锁或原子操作。

  2. 虽然volatile能够确保可见性,但不能保证原子性。如果需要执行一系列操作并保证原子性,应考虑使用原子操作类。

  3. 过度使用volatile关键字可能会影响性能,因此应谨慎使用,仅在必要时使用。

  4. volatile关键字不能替代锁机制,它们各有各的应用场景。

总结

volatile关键字和原子操作是多线程编程中的重要概念,它们用于确保线程之间的可见性和数据一致性。volatile关键字用于声明一个变量是"易失性"的,确保对该变量的修改对其他线程是可见的。原子操作则提供了一系列不可分割的操作,保证了操作的原子性。

合理地使用volatile关键字和原子操作可以提高多线程程序的性能和可靠性,但需要根据具体情况选择合适的方式。同时,也需要注意volatile关键字并不能替代锁机制,它们各有各的应用场景。在多线程编程中,保持谨慎和小心是非常重要的。希望本文能帮助您更好地理解volatile关键字和原子操作,以及它们在多线程编程中的应用。

相关文章:

【Java 基础篇】Java线程:volatile关键字与原子操作详解

在多线程编程中&#xff0c;确保线程之间的可见性和数据一致性是非常重要的。Java中提供了volatile关键字和原子操作机制&#xff0c;用于解决这些问题。本文将深入讨论volatile关键字和原子操作的用法&#xff0c;以及它们在多线程编程中的重要性和注意事项。 volatile关键字…...

992. K 个不同整数的子数组

992. K 个不同整数的子数组 给定一个正整数数组 nums和一个整数 k&#xff0c;返回 nums 中 「好子数组」 的数目。 如果 nums 的某个子数组中不同整数的个数恰好为 k&#xff0c;则称 nums 的这个连续、不一定不同的子数组为 「好子数组 」。 例如&#xff0c;[1,2,3,1,2] 中…...

Vue 使用vue-cli构建SPA项目(超详细)

目录 一、什么是vue-cli 二&#xff0c;构建SPA项目 三、 运行SPA项目 前言&#xff1a; 在我们搭建SPA项目时候&#xff0c;我们必须去检查我们是否搭建好NodeJS环境 cmd窗口输入以下指令&#xff1a;去检查 node -v npm -v 一、什么是vue-cli Vue CLI&#xff08;Vu…...

SpringBoot工程模板

spring脚手架&#xff1a;https://start.spring.io/ <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocati…...

学习SLAM:SLAM进阶(十)暴力更改ROS中的PCL库

话不多说&#xff0c;上活 1.1 为什么要这么做 项目中有依赖。。。。 1.2 安装VTK7.1.1 PCL1.8.0 略 1.3 移植到ROS 删除ROS依赖的vtk6.2和PCL1.8.0的动态链接库&#xff1a; liugongweiubuntu:~$ sudo mv /usr/lib/x86_64-linux-gnu/libvtk* Desktop/lib/ [sudo] password fo…...

js 事件流、事件冒泡、事件捕获、阻止事件的传播

事件流 js 事件的执行过程分为捕获阶段&#xff08;由外层节点传播到内层节点&#xff09;和冒泡阶段&#xff08;由内层节点传播到外层节点&#xff09;&#xff0c;即先执行捕获阶段的代码&#xff0c;后执行冒泡阶段的代码 事件冒泡 js 事件中的代码默认在冒泡阶段执行&…...

一家美国公司被黑,一个拉美国家政务服务瘫痪

政务系统承包商遭勒索攻击&#xff0c;导致哥伦比亚国家政务服务陷入瘫痪。 据报道&#xff0c;9月19日哥伦比亚的多个重要政府部门正在应对一次勒索软件攻击&#xff0c;官员们被迫大幅变更部门运作方式。 哥伦比亚卫生和社会保护部、司法部门、工商监管部门上周宣布&#x…...

c++ QT 十八位时间戳转换

先说一下UTC&#xff1a; 它是协调世界时间&#xff0c;又称世界统一时间、世界标准时间、国际协调时间&#xff0c;简称UTC UTC时间与本地时间关系&#xff1a;UTC 时间差本地时间 如果UTC时间是 2015-05-01 00:00:00 那么北京时间就是 2015-05-01 08:00:00 解释&#xff1a;…...

全国职业技能大赛云计算--高职组赛题卷④(容器云)

全国职业技能大赛云计算--高职组赛题卷④&#xff08;容器云&#xff09; 第二场次题目&#xff1a;容器云平台部署与运维任务1 Docker CE及私有仓库安装任务&#xff08;5分&#xff09;任务2 基于容器的web应用系统部署任务&#xff08;15分&#xff09;任务3 基于容器的持续…...

【TCP】延时应答 与 捎带应答

延时应答 与 捎带应答 一. 延迟应答&#xff08;效率机制&#xff09;二. 捎带应答&#xff08;效率机制&#xff09; 一. 延迟应答&#xff08;效率机制&#xff09; 延时应答&#xff1a;相当于 流量控制 的延伸。 流量控制是 踩下了刹车&#xff0c;是发送方发的不要太快&a…...

URL与URI小结

文章目录 一、URL是什么&#xff1f;URL的一般形式&#xff1a; 二、分类三、URI总结 一、URL是什么&#xff1f; 每条由Web服务器返回的内容都是和它管理的某个文件相关联的&#xff0c;这些文件中的每一个都有一个唯一的名字&#xff0c;叫做URL&#xff08;通用资源定位符&…...

QT--day5

注册 mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include<QPushButton> #include<QLineEdit> #include<QLabel> #include <QMessageBox> #include<QString> #include<QSqlDatabase> …...

在windows和linux上玩转Tensorrt

为避免重复&#xff0c;一些安装内容我直接贴其他大佬的帖子了&#xff0c;我是按照他们的步骤来操作的&#xff0c;趟过一遍&#xff0c;没有问题。 本篇着重在tensort在Cmakelist中如何配置&#xff0c;以及如何配置编译动/静态库&#xff0c;比较基础&#xff0c;也是想做个…...

七天学会C语言-第五天(函数)

1. 调用有参函数 有参函数是一种接受输入参数&#xff08;参数值&#xff09;并执行特定操作的函数。通过向函数传递参数&#xff0c;你可以将数据传递给函数&#xff0c;让函数处理这些数据并返回结果。 例1&#xff1a;编写一程序&#xff0c;要求用户输入4 个数字&#xf…...

340. 至多包含 K 个不同字符的最长子串

340. 至多包含 K 个不同字符的最长子串 vip...

【分布式计算】副本数据Replicated Data

作用&#xff1a;可靠性、高性能、容错性 问题&#xff1a;如何保持一致、如何更新 问题&#xff1a;存在读写/写写冲突 一个简单的方法就是每个操作都保持顺序&#xff0c;但是因为网络延迟会导致问题 Data-centric models: consistency model?? ??? 读取时&#xff0c…...

erlang练习题(二)

题目一 替换元组或列表中指定位置的元素&#xff0c;新元素作为参数和列表或元组一起传入函数内 解答 replaceIdx(List, Index, Val) ->replaceIdx(List, Index, Val, 1, []).replaceIdx([], _, _, _, Acc) ->lists:reverse(Acc);%% 到达替换位置的处理replaceIdx([_ …...

CRM软件系统价格不同的原因

很多人在了解CRM系统时&#xff0c;发现不同品牌的CRM价格有着很大的区别。一些CRM系统只需要几千块钱&#xff0c;一些CRM系统的报价却要上万&#xff0c;甚至十几万。为什么CRM系统价格不同&#xff1f;下面我们就来说说。 1、功能不同 从功能方面来说&#xff0c;一些CRM系…...

json数据解析

目录 一、读数据 1、简单对象读取 2、数组读取 3、对象读取 二、写数据 1、简单生成JSON 2、对象数组JSON 3、嵌套对象 三、一个综合例子 1、读JSON 2、写JSON 一、读数据 1、简单对象读取 {"app": "xnwVideo","src": "C:\\buil…...

Verilog零基础入门(边看边练与测试仿真)-状态机-笔记(7-10讲)

文章目录 第七讲第八讲第九讲第十讲 第七讲 1、最简单的状态机-三角波发生器 1、两种状态的代码&#xff1a; //最简单的状态机&#xff0c;三角波发生器&#xff1b; timescale 1ns/10ps module tri_gen(clk,res,d_out); input clk; input res; o…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

论文笔记——相干体技术在裂缝预测中的应用研究

目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术&#xff1a;基于互相关的相干体技术&#xff08;Correlation&#xff09;第二代相干体技术&#xff1a;基于相似的相干体技术&#xff08;Semblance&#xff09;基于多道相似的相干体…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...