synchronized 与 volatile 关键字
目录
- 1.前言
- 1.synchronized 关键字
- 1. 互斥
- 2.保证内存可见性
- 3.可重入
- 2. volatile 关键字
- 1.保证内存可见性
- 2.无法保证原子性
- 3.synchronized 与 volatile 的区别
1.前言
synchronized关键字和volatile是大家在Java多线程学习时接触的两个关键字,很多同学可能学习完就忘记了,本文帮助大家回顾以及学习两个关键字的作用,以及说出它们的区别,同时也为了自己学习巩固。
1.synchronized 关键字
1. 互斥
属于synchronized最关键的特性,可以起到互斥的作用,当某个线程执行到某个对象的synchronized中时,其他线程如果也执行到同一个对象 的synchronized 时就会进行阻塞等待。
- 进入
synchronized修饰的代码块此时相当于 加锁 - 退出
synchronized修饰的代码块此时相当于 释放锁
其解决的问题是在多线程环境下,多个线程对于同一个变量进行读写操作时可能产生的线程安全问题。
如下图代码:
public class Main {static int count = 0;static void add() {count++;}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {add();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {add();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}
}
按照预期,我们希望count的值应该是100000,当执行后输出的答案却是:

无论多次执行多少次,答案总是与预期相差甚远。这是最简单的多线程安全问题。因为count++这个操作,需要先将count从主内存读入到工作内存,然后增值,再将更改后的值写回主内存,这一系列操作必须保证原子性,而在多线程环境下是无法保证的,所以我们需要加上synchronized进行上锁,以此保证自增这个操作的原子性。
只需更改add方法如下:
synchronized static void add() {count++;}
再次运行后答案与预期相符合:

需要注意一点,synchronized修饰方法时如果是静态方法,则加的是该类对象的锁,如果是成员方法,则加的是对象锁。
2.保证内存可见性
从上面也可以看出synchronized的工作过程:
- 1.获得互斥锁
- 2.从主内存拷贝变量的最新副本到工作内存
- 3.执行代码
- 4.讲更改后的共享变量的值更新回工作内存
- 5.释放互斥锁
这样的工作流程,是一定可以保证内存可见性的。当然有的同学并不了解什么是内存可见性,下文讲volatile时我们稍微讲一下,因为它也能保证内存可见性。
3.可重入
synchronized同步块,对于同一条线程来说是可重入的,不会出现将自身锁死的情况。
当然大家可能对 自身锁死 这个情况不太理解,我们举例一个代码:
public class Main {//锁对象public static Object lock = new Object();public static void main(String[] args) {//一次加锁synchronized (lock) {//二次加锁synchronized (lock) {System.out.println("正确输出");}}}
}
当线程在一次加锁时,会成功加锁,当第二次加锁时,此时lock已被上锁,于是该线程进行阻塞等待,但其实这个锁是被它自己拿着的,它又不进行释放锁操作,于是将自己锁死。这样的锁称之为 不可重入锁。
当我们Java中的synchronized是可重入锁,不会出现上面的问题,它可以正确打印:

如果对上述代码还不够理解,可以再看一个二次加锁的例子:
public class Main {public int count = 0;synchronized void increase() {count++;}synchronized void increase2() {increase();}
}
在上诉代码中:
increase和increase2两个方法都加了synchronized,而且它们的锁对象都是针对当前对象加锁的。- 在调用
increase2时,会先给该对象上锁,执行调用increase时,会二次上锁(此时上个锁还未释放),这是没问题的,因为synchronized是可重入锁。
那是否真的上了两把锁呢?
其实并非如此,在可重入锁的内部,包含了 线程持有者 和 计数器 两个信息。
- 如果某个线程加锁时,发现锁已被占用,但又发现占用的恰好是自己时,那么然后可以获取到这个锁,并让计数器自增
- 解锁时首先会让计数器自减,但只有真正自减到
0时,我们才会真正意义上的将该锁释放,以供其他线程获取到。
2. volatile 关键字
相对于 synchronized来说,大家可能对volatile会比较陌生,我们来看看其有哪些作用。
1.保证内存可见性

简单来说,线程在工作时,会去主内存中读取数据到工作内存中,然后从工作内存读取数据。但是,线程从工作内存读取数据的速度,要远远的大于从主内存读取数据。
当一个线程大量地从主内存请求同一个变量的值时,它会发现这个值一直没变,此时jvm会 “自作主张” 的进行优化,直接从工作内存读取之前读到的值。这就会导致一个问题,其他线程对这个共享变量值进行修改,这个线程不能及时地被看到,也就读到了一个错误的值。
比如如下代码:
public class Main{static int isQuit = 0;public static void main(String[] args) {Thread t = new Thread(() -> {while (isQuit == 0) {}System.out.println("t线程执行结束");});t.start();Scanner sc = new Scanner(System.in);isQuit = sc.nextInt();System.out.println("main线程执行结束");}
}
执行以后随便输入一个非零整数:

发现t线程仍然在进行,而main线程已经结束,但按照逻辑其实此时isQuit值被修改为非零,t线程也应该结束。这就是由于内存可见性产生的问题,main线程修改了isQuit的值t线程并不能及时的接收到。
解决的方法也很简单,只需要给isQuit加上volatile关键字,这样每次t线程都会强制去主内存中读取isQuit的值,从而保证了内存可见性。
2.无法保证原子性
volatile相较于synchronized来说,主要在于其无法保证原子性,也就是对于下面这个程序,即使给count加上volatile,我们也无法让count的值为100000。
public class Main {static int count = 0;static void add() {count++;}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {add();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {add();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}
}
3.synchronized 与 volatile 的区别
根据上面的总结,我们可知:synchronized既可以保证原子性还可以保证内存可见性,而volatile只能保证内存可见性。那有的人是不是肯定想,那我们无脑使用synchronized不就好了吗?
那肯定不对,synchronized会进行加锁,使得效率大大的较低,而volatile却不会影响效率,所以只有必须要保证原子性的操作,我们才选择synchronized进行加锁。
相关文章:
synchronized 与 volatile 关键字
目录1.前言1.synchronized 关键字1. 互斥2.保证内存可见性3.可重入2. volatile 关键字1.保证内存可见性2.无法保证原子性3.synchronized 与 volatile 的区别1.前言 synchronized关键字和volatile是大家在Java多线程学习时接触的两个关键字,很多同学可能学习完就忘记…...
【0成本搭建个人博客】——Hexo+Node.js+Gitee Pages
目录 1、下载安装Git 2、下载安装Node.js 3、使用Hexo进行博客的搭建 4、更改博客样式 5、将博客上传到Gitee 6、更新博客 首先看一下Hexo的博客的效果。 1、下载安装Git Git 是一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本…...
【面试实战】认证授权流程及原理分析
认证授权流程及原理分析 1、认证 (Authentication) 和授权 (Authorization)的区别是什么?2、什么是Cookie ? Cookie的作用是什么?如何在服务端使用 Cookie ?3、Cookie 和 Session 有什么区别?如何使用Session进行身份验证?1、认证 (Authentication) 和授权 (Authorizatio…...
TPM命令解析之tpm2_startauthsession
参考网址链接:tpm2-tools/tpm2_startauthsession.1.md at master tpm2-software/tpm2-tools GitHub 命令名称 tpm2_startauthsession 功能 启动一个TPM会话。 命令形式 tpm2_startauthsession [OPTIONS] 描述 启动一个TPM会话。默认是启动一个试验(…...
第14章 局部波动率模型
这学期会时不时更新一下伊曼纽尔德曼(Emanuel Derman) 教授与迈克尔B.米勒(Michael B. Miller)的《The Volatility Smile》这本书,本意是协助导师课程需要,发在这里有意的朋友们可以学习一下,思…...
云原生周刊:开源“赢了”,但它可持续吗?
日前召开的 State of Open 会议上,开源“赢了”,但如果政府和企业不站出来确保生态系统在未来的弹性和可持续性,那么它仍然会失败。 OpenUK 首席执行官 Amanda Brock 在开幕式上表示,数字化和开源在过去 5 到 10 年的进步提升了工…...
读《企业IT架构转型之道》
本书还没读完,暂摘抄一些概念,因为自身做的新系统也在转型,从单体式到一体化一年来遇到很多问题有技术上的,也有团队协作的,过程是痛苦且复杂的,所以在刚翻阅前几十页时候,对于淘宝技术团队转型…...
Qt中的QTcpSocket、QWebSocket和QLocalSocket
同时实现了QTcpSocket、QWebSocket和QLocalSocket的简单通讯deamon,支持自动获取本机ip,多个客户端交互。在这个基础上你可以自己加错误检测、心跳发送、包封装解析和客户端自动重连等功能。 获取本机电脑ip: QString Widget::getIp() {QSt…...
枚举学习贴
1. 概述 1.1 是什么 枚举对应英文(enumeration, 简写 enum)枚举是一组常量的集合。可以这里理解:枚举属于一种特殊的类,里面只包含一组有限的特定的对象 1.2 枚举的二种实现方式 自定义类实现枚举使用 enum 关键字实现枚举 1.3 什么时候用 存在有限…...
【C++】30h速成C++从入门到精通(继承)
继承的概念及定义继承的概念继承(inheritance)机制是面向对象程序设计使代码可以复用的重要手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序…...
Java多线程还不会的进来吧,为你量身打造
💗推荐阅读文章💗 🌸JavaSE系列🌸👉1️⃣《JavaSE系列教程》🌺MySQL系列🌺👉2️⃣《MySQL系列教程》🍀JavaWeb系列🍀👉3️⃣《JavaWeb系列教程》…...
8 神经网络及Python实现
1 人工神经网络的历史 1.1 生物模型 1943年,心理学家W.S.McCulloch和数理逻辑学家W.Pitts基于神经元的生理特征,建立了单个神经元的数学模型(MP模型)。 1.2 数学模型 ykφ(∑i1mωkixibk)φ(WkTXb)y_{k}\varphi\left(\sum_{i1…...
使用QIS(Quantum Image Sensor)图像重建总结(1)
最近看了不少使用QIS重建图像的文章,觉得比较完整详细的还是Abhiram Gnanasambandam的博士论文:https://hammer.purdue.edu/articles/thesis/Computer_vision_at_low_light/20057081 1 介绍 讲述了又墨子的小孔成像原理,到交卷相机…...
【SpringCloud】SpringCloud教程之Nacos实战(二)
目录前言一.Nacos实现配置管理二.Nacos拉取配置三.Nacos配置热更新(自动刷新,不需要重启服务)1.在有Value注入变量所在类添加注解2.新建类用于属性加载和配置热更新四.Nacos多环境配置共享1.多环境共享配置2.配置的加载优先级测试3.配置优先级前言 Nacos实战一&…...
利用Qemu工具仿真ARM64平台
Windows系统利用Qemu仿真ARM64平台0 写在最前1 Windows安装Qemu1.1 下载Qemu1.2 安装Qemu1.3 添加环境变量1.4测试安装是否成功2. Qemu安装Ubuntu-Server-Arm-642.1 安装前的准备2.2 安装Ubuntu server arm 64位镜像3 Windows配置Qemu网络和传输文件3.1 参考内容3.2 Windows安装…...
【Hello Linux】进程控制 (内含思维导图)
作者:小萌新 专栏:Linux 作者简介:大二学生 希望能和大家一起进步! 本篇博客简介:简单介绍下进程的控制 包括进程启动 进程终止 进程等待 进程替换等概念 进程控制介绍进程创建fork函数fork函数的返回值fork函数的使用…...
嵌入式linux物联网毕业设计项目智能语音识别基于stm32mp157开发板
stm32mp157开发板FS-MP1A是华清远见自主研发的一款高品质、高性价比的Linux单片机二合一的嵌入式教学级开发板。开发板搭载ST的STM32MP157高性能微处理器,集成2个Cortex-A7核和1个Cortex-M4 核,A7核上可以跑Linux操作系统,M4核上可以跑FreeRT…...
【黄河流域公安院校网络空间安全技能挑战赛】部分wp
文章目录webbabyPHPfunnyPHPEzphp**遍历文件目录的类**1、DirectoryIterator:2、FilesystemIterator:3、**Globlterator**读取文件内容的类:SplFileObjectMisc套娃web babyPHP <?php highlight_file(__FILE__); error_reporting(0);$num $_GET[nu…...
五点CRM系统核心功能是什么
很多企业已经把CRM客户管理系统纳入信息化建设首选,用于提升核心竞争力,改善企业市场、销售、服务、渠道和客户管理等几个方面,并进行创新或转型。CRM系统战略的五个关键要点是:挖掘潜在客户、评估和培育、跟进并成交、分析并提高…...
window.print() 前端实现网页打印详解
目录 前言 一、print()方法 二、打印样式 2.1使用打印样式表 2.2使用媒介查询 2.3内联样式使用media属性 2.4在css中使用import引入打印样式表 三、打印指定区域部分内容 3.1方法一 3.2方法二 3.3方法三 四、强制插入分页 4.1page-break-before(指定元素前…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
