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

精心修炼Java并发编程(JUC)-volatile与synchronized关键字

volatile

volatile 是 JVM 提供的 最轻量级的同步机制,中文意思是不稳定的,易变的,用 volatile 修饰变量是为了保证变量在多线程中的可见性,它表达的含义是:告诉编译器,对这个变量的读写,需要基于主内存保证多CPU的缓存一致性。

volatile 变量的两个特性:解决可见性和有序性

1、解决可见性

保证变量对所有线程的可见性:当一条线程修改了 volatile 变量的值,新值对于其他线程来说是可以立即得知的。而普通变量不能做到这一点

线程写 volatile 变量的过程:

  1. 改变线程工作内存中 volatile 变量副本的值
  2. 将改变后的副本的值立即从工作内存刷新到主内存

线程读 volatile 变量的过程:

  1. 从主内存中读取 volatile 变量的最新值到线程的工作内存中
  2. 从工作内存中读取 volatile 变量的副本

注意:

1、volatile并不能保证并发操作的原子性,即不保证线程安全

2、volatile修饰引用类型,它只能保证引用本身的可见性,不能保证所引用对象内部属性的可见性

2、解决有序性

禁止进行指令重排序,具体一点解释,禁止重排序的规则如下:

  • volatile变量时,可以确保volatile写之前的操作不会被编译器重排序到volatile写之后。
  • volatile变量时,可以确保volatile读之后的操作不会被编译器重排序到volatile读之前。

synchronized

如果某一个资源被多个线程共享,为了避免因为资源抢占导致资源数据错乱,我们需要对线程进行同步,那么synchronized就是实现线程同步的关键字

synchronized的作用是保证在同一时刻, 被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的效果。

其用法如下:

从语法上讲,Synchronized可以把任何一个非null对象作为"锁",在HotSpot JVM实现中,锁有个专门的名字:对象监视器(Object Monitor)

// (1)修饰静态方法
public synchronized static void helloStatic(){System.out.println("hello world static");
}// (2)修饰成员函数
public synchronized void hello(){System.out.println("hello world");
}// (3)直接定义代码块
public void test(){SynchronizedTest test = new SynchronizedTest();        synchronized (test){System.out.println("hello world");}
}

synchronized其底层说白了就是锁,所以呢,我们先从锁讲起

我们知道发生原子性的根源是CPU在执行完任意指令后都有可能发生线程切换。如果能够禁用线程切换的话那这个问题也就迎刃而解了。操作系统做线程切换是依赖 CPU 中断的,所以禁止 CPU 发生中断就能够禁止线程切换。

1、加锁和解锁操作在哪里体现的?

synchronized 的加锁和解锁是隐式实现的,可以查看字节码

2、synchronized 的锁对象是什么,也就是说锁定的是哪个对象?

  • 如果修饰的是代码块,锁对象是我们自己指定的,指定哪个对象就锁定哪个对象。
  • 如果修饰的是非静态方法,锁定的是当前实例对象 this
  • 如果修饰的是静态方法,锁定的是当前类的 Class 对象。

Linux内核提供的锁机制

1、Mutex(互斥量):pthread_mutex_t,通过对该结构的操作,来判断资源是否可以访问,Mutex属于sleep-waiting类型的锁,例如在多核机器上有两个线程A,B,如果此时锁被A持有,那么B就会被阻塞,在等待队列中等待。

2、Spin lock(自旋锁):pthread_spinlock_t,属于busy-waiting类型的锁,它不会引起调用者睡眠等待,如果获取不到锁则进入忙等待,它会不停的尝试去获取锁,俗称自旋,获取锁的性能相对较高,但是费CPU,所以自旋锁不应该被长时间的持有。

3、 Condition Variable(条件变量):pthread_cond_t,条件变量是利用线程间共享的全局变量,进行同步的一种机制

4、Read/Write Lock(读写锁):pthread_rwlock_t,读写锁是用来解决读多写少问题的,读操作可以共享,写操作是排他的。

5、semaphore:sem_t,信号量(semaphore)机制,也可用于互斥锁的实现

synchronized底层

public static void synClass() {Object obj = new Object();synchronized (obj) {}
}

我们看看这段代码的字节码文件

 0 new #2 <java/lang/Object>3 dup4 invokespecial #1 <java/lang/Object.<init> : ()V>7 astore_18 aload_19 dup
10 astore_2
11 monitorenter
12 aload_2
13 monitorexit
14 goto 22 (+8)
17 astore_3
18 aload_2
19 monitorexit
20 aload_3
21 athrow
22 return

其中跟synchronized关键字相关的就是这样的字节码

monitorenter
........
monitorexit

monitorenter主要是获取监视器锁,monitorexit主要是释放监视器锁,所以我们的synchronized就是去获取这个监控锁对象,他会在我们的对象头中(markword)

markword图示如下:

加锁过程

前面我们看到了synchronized在字节码层面是对应monitorentermonitorexit,而真正实现互斥的锁其实依赖操作系统底层的Mutex Lock来实现,首先要明确一点,这个锁是一个重量级的锁

整体锁升级的过程大致可以分为两条路径,如下:

1、偏向锁未启动,加锁默认加轻量级锁

轻量级锁:线程在自己的线程栈生成Lock Record,使用CAS的方式将markword设置为指向自己线程LOCK Record的指针,设置成功者得锁。竞争会让锁膨胀为重量级锁。

2、偏向锁启动

偏向锁,偏向的是第一个来获取锁的线程。所谓偏向锁,指的是获取锁的线程在markword中写自己的线程ID的过程,偏向锁升级为轻量级锁时首先要撤销偏向锁,如何设置轻量级锁。

偏向锁默认是打开的,但是启动有一个时延,默认4s,之所以要延迟,是因为JVM虚拟机自己有一些默认的启动线程,里面有好多sync代码,这些代码启动时就肯定会有竞争,如果直接使用偏向锁,就会造成偏向锁不断的进行锁撤销和锁升级的操作,效率较低。

我们平时使用synchronized注意点总结如下:

  • 降低锁的等级

    能用对象级别的,尽量别用类锁,能用实例变量的不要用静态变量

  • 减少锁的时间 不需要同步执行的代码,能不放在同步块里面执行就不要放在同步快内,可以让锁尽快释放

  • 减少锁的粒度 共享资源数决定锁的数量。有一组资源定义一把锁,而不是多组资源共用一把锁,增加并行度,从而降低锁竞争,典型如分段锁

  • 减少加减锁的次数 假如有一个循环,循环内的操作需要加锁,我们应该把锁放到循环外面,否则每次进出循环,都要加锁

  • 读写锁 业务细分,读操作加读锁,可以并发读,写操作使用写锁

  • 善用volatile

    volatile的控制比synchronized更轻量化,在某些变量上不涉及多步打包操作和原子性问题,可以加以运用。

    如ConcurrentHashMap的get操作,使用的volatile而不是加锁

相关文章:

精心修炼Java并发编程(JUC)-volatile与synchronized关键字

volatile volatile 是 JVM 提供的 最轻量级的同步机制&#xff0c;中文意思是不稳定的&#xff0c;易变的&#xff0c;用 volatile 修饰变量是为了保证变量在多线程中的可见性&#xff0c;它表达的含义是&#xff1a;告诉编译器&#xff0c;对这个变量的读写&#xff0c;需要基…...

【ROS2】ROS2 与 ROS1 编码方式对比(Python实现)

目录 一、初始化和关闭节点二、发布者三、订阅者四、服务端五、客户端六、参数管理七、日志记录八、生命周期管理 ROS2 在 Python 编程中引入了一些新的概念和 API&#xff0c;这些变化使得代码更加模块化和易于维护。特别是 rclpy 库提供了更丰富的功能和更好的错误处理机制&a…...

ElasticSearch的下载和基本使用(通过apifox)

1.概述 一个开源的高扩展的分布式全文检索引擎&#xff0c;近乎实时的存储&#xff0c;检索数据 2.安装路径 Elasticsearch 7.8.0 | Elastic 安装后启动elasticsearch-7.8.0\bin里的elasticsearch.bat文件&#xff0c; 启动后就可以访问本地的es库http://localhost:9200/ …...

城市轨道交通运营控制指挥中心设计方案

为某城市轨道交通运营控制指挥中心(OCC)的设计提供方案时,我们需要考虑到多个方面的需求,包括系统架构、设备选择、功能实现、数据流与监控、通信管理等。以下是一个综合性的设计方案,涉及系统硬件和软件的选择、布局规划、安全性等方面,以确保指挥中心的高效运作、实时监…...

多目标优化算法:多目标河马优化算法(MOHOA)求解ZDT1、ZDT2、ZDT3、ZDT4、ZDT6,提供完整MATLAB代码

一、河马优化算法 河马优化算法&#xff08;Hippopotamus optimization algorithm&#xff0c;HO&#xff09;由Amiri等人于2024年提出的一种模拟自然界中河马觅食行为的新型群体智能优化算法。该算法由Mohammad Hussein Amiri等人于2024年2月发表在Nature旗下子刊《Scientifi…...

线程与进程的个人理解

进程&#xff08;Process&#xff09;&#xff1a; 一个程序在执行时&#xff0c;操作系统为其分配的资源&#xff08;如内存、CPU 时间等&#xff09;构成了一个进程。每个进程都有自己的独立的地址空间、堆栈和局部变量&#xff0c;它们之间不共享内存&#xff08;除非通过特…...

vscode的项目给gitlab上传

目录 一.创建gitlab帐号 二.在gitlab创建项目仓库 三.Windows电脑安装Git 四.vscode项目git上传 一.创建gitlab帐号 二.在gitlab创建项目仓库 图来自:Git-Gitlab中如何创建项目、创建Repository、以及如何删除项目_gitlab新建项目-CSDN博客&#xff09; 三.Windows电脑安…...

企业微信定位打卡

废话少说&#xff1a;定位修改软件链接奉上 一、定位打卡原理 GPS定位&#xff1a;企业微信可以利用手机的GPS功能进行定位&#xff0c;这是一种基于卫星的定位技术&#xff0c;能够提供相对精确的位置信息&#xff0c;通常精确度在20米以内。这种方式耗电较大&#xff0c;且在…...

libaom 源码分析:码率控制介绍

码率控制 命令行码率控制选项:可以看到码率控制包括丢帧、resize、超分、码控模式、目标码率、目标上限下限(类似 x264、x265 中的 VBV)、码控偏置、GOP 码率等。Rate Control Options:--drop-frame=<arg> Temporal resampling threshold (buf %)--resize-mo…...

RK3568平台开发系列讲解(DMA篇)DMA engine使用

🚀返回专栏总目录 文章目录 一、申请DMA channel二、配置DMA channel的参数三、获取传输描述(tx descriptor)四、启动传输沉淀、分享、成长,让自己和他人都能有所收获!😄 📢DMA子系统下有一个帮助测试的测试驱动(drivers/dma/dmatest.c), 从这个测试驱动入手我们了解…...

C++中的函数对象

C 中函数对象的定义和特点 定义&#xff1a;函数对象&#xff08;Function Object&#xff09;也叫仿函数&#xff08;Functor&#xff09;&#xff0c;是一个类&#xff0c;这个类重载了函数调用运算符()。当创建这个类的对象后&#xff0c;可以像使用函数一样使用这个对象&am…...

Linux指标之平均负载(The Average load of Linux Metrics)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…...

盛最多水的容器

本节将数组与坐标轴共同组成一个容器,通过改变容器的两个端点使容器装的水最多,容器两个端点不断移动可以通过左右指针算法解决. 问题描述: 给定两个非负整数k1,k2...km每个数代表坐标中的一个点(i,ki).在坐标内绘制m条垂线,垂直线i的两个端点分别为(i,k1)和(i,0)找出其中的两…...

光伏功率预测!Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型时序预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型多变量时序光伏功率预测 (Matlab2023b 多输入单输出) 1.程序已经调试好&#xff0c;替换数据集后&#xff0c;仅运行一个main即可运行&#xff0c;数据格式…...

java全栈day10--后端Web基础(基础知识)

引言&#xff1a;只要能通过浏览器访问的网站全是B/S架构&#xff0c;其中最常用的服务器就是Tomcat 在浏览器与服务器交互的时候采用的协议是HTTP协议 一、Tomcat服务器 1.1介绍 官网地址&#xff1a;Apache Tomcat - Welcome! 1.2基本使用(网上有安装教程&#xff0c;建议…...

使用爬虫时,如何确保数据的准确性?

在数字化时代&#xff0c;数据的准确性对于决策和分析至关重要。本文将探讨如何在使用Python爬虫时确保数据的准确性&#xff0c;并提供代码示例。 1. 数据清洗 数据清洗是确保数据准确性的首要步骤。在爬取数据后&#xff0c;需要对数据进行清洗&#xff0c;去除重复、无效和…...

Burp入门(4)-扫描功能介绍

声明&#xff1a;学习视频来自b站up主 泷羽sec&#xff0c;如涉及侵权马上删除文章 感谢泷羽sec 团队的教学 视频地址&#xff1a;burp功能介绍&#xff08;1&#xff09;_哔哩哔哩_bilibili 本文介绍burp的主动扫描和被动扫描功能。 一、主动扫描 工作原理&#xff1a; 主动…...

Tourtally:颠覆传统的AI智能旅行规划革命

# Tourtally&#xff1a;颠覆传统的AI智能旅行规划革命 在快速变化的旅行科技世界里&#xff0c;一个划时代的平台正在重新定义我们探索世界的方式。让我们一起认识 Tourtally&#xff0c;这个由人工智能驱动的旅行规划助手&#xff0c;正在彻底改变旅行体验。 ## 旅行规划的…...

chrome允许http网站打开摄像头和麦克风

第一步 chrome://flags/#unsafely-treat-insecure-origin-as-secure 第二步 填入网址&#xff0c;点击启用 第三步 重启 Chrome&#xff1a;设置完成后&#xff0c;点击页面底部的 “Relaunch” 按钮&#xff0c;重新启动 Chrome 浏览器&#xff0c;使更改生效。...

视觉经典神经网络与复现:深入解析与实践指南

目录 引言 经典视觉神经网络模型详解 1. LeNet-5&#xff1a;卷积神经网络的先驱 LeNet-5的关键特点&#xff1a; 2. AlexNet&#xff1a;深度学习的突破 AlexNet的关键特点&#xff1a; 3. VGGNet&#xff1a;深度与简洁的平衡 VGGNet的关键特点&#xff1a; 4. ResNe…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…...

Python常用模块:time、os、shutil与flask初探

一、Flask初探 & PyCharm终端配置 目的: 快速搭建小型Web服务器以提供数据。 工具: 第三方Web框架 Flask (需 pip install flask 安装)。 安装 Flask: 建议: 使用 PyCharm 内置的 Terminal (模拟命令行) 进行安装,避免频繁切换。 PyCharm Terminal 配置建议: 打开 Py…...

Python 高级应用10:在python 大型项目中 FastAPI 和 Django 的相互配合

无论是python&#xff0c;或者java 的大型项目中&#xff0c;都会涉及到 自身平台微服务之间的相互调用&#xff0c;以及和第三发平台的 接口对接&#xff0c;那在python 中是怎么实现的呢&#xff1f; 在 Python Web 开发中&#xff0c;FastAPI 和 Django 是两个重要但定位不…...