Java中synchronized:特性、使用、锁机制与策略简析
目录
- synchronized的特性
- 互斥性
- 可见性
- 可重入性
- synchronized的使用方法
- synchronized的锁机制
- 常见锁策略
- 乐观锁与悲观锁
- 重量级锁与轻量级锁
- 公平锁与非公平锁
- 可重入锁与不可重入锁
- 自旋锁
- 读写锁
synchronized的特性
互斥性
synchronized确保同一时间只有一个线程可以进入同步块或同步方法,避免了多线程并发访问共享资源的冲突问题。
synchronized 会起到互斥效果,某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到同一个对象 synchronized 就会阻塞等待。
下面我们来看一个例子,两个线程获取同一个锁,锁被占用后,剩下的那个线程就会进行阻塞等待。
public class test2 {public static void main(String[] args) {Object object = new Object();Thread t1 = new Thread(()->{//进入 synchronized 修饰的代码块, 相当于 加锁synchronized (object) {for (int i = 0; i < 5; i++) {System.out.println("线程t1获取锁");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}//退出 synchronized 修饰的代码块, 相当于 解锁 });Thread t2 = new Thread(()->{synchronized (object) {for (int i = 0; i < 5; i++) {System.out.println("线程特t2获取锁");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});t1.start();t2.start();}
}
由结果我们可以知道,线程一释放锁后,由操作系统唤醒线程二才能获取到锁。
synchronized的底层是使用操作系统的mutex lock实现的。
可见性
内存可见性是指当一个线程修改了共享变量的值后,其他线程能够立即看到修改的值。在多线程环境中,由于多个线程同时访问共享变量,每个线程都有自己的工作内存,而工作内存中保存了主内存中的部分数据副本。因此,当一个线程修改了共享变量的值,但这个修改尚未被刷新到主内存时,其他线程可能无法立即看到这个修改,而继续使用自己工作内存中的旧值,造成了内存不可见性。
synchronized 既能保证原子性,也能保证内存可见性,一个线程对共享变量的修改对于其他线程是可见的。
class Counter {public static int flag = 0;
}public class test3 {public static void main(String[] args) {Object object = new Object();Thread t1 = new Thread(() -> {while (true) {synchronized (object) {if (Counter.flag != 0) {break;}}}System.out.println("线程一知道了共享变量改为" + Counter.flag);});Thread t2 = new Thread(() -> {Scanner scanner = new Scanner(System.in);System.out.println("输入一个整数:");Counter.flag = scanner.nextInt();});t1.start();t2.start();}
}
如果线程一不加synchronized,那么共享变量的改变它就感知不到,以至于程序一直在运行中。
可重入性
synchronized 同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题。
可以理解为一个线程没有释放锁,然后又尝试再次加锁。
按照之前对锁的理解就是,锁没有释放,进行再次加锁就会进行阻塞,直到第一次的锁被释放,才能获取到第二个锁,但释放第一个锁也由该线程来进行,结果现在这个线程啥都干不了,也就只能形成死锁了。
这样的锁称其为不可重入锁。
我们的synchronized是可重入锁。
在重入锁的内部有两个信息,分别为“程序计数器”和“线程持有者”
- 如果某个线程加锁的时候,发现锁已经被人占用,但是恰好占用的正是自己, 那么仍然可以继续获取到锁,并让计数器自增。
- 解锁的时候计数器递减为 0 的时候,才真正释放锁。
synchronized的使用方法
- 直接修饰普通方法: 锁的 SynchronizedDemo 对象
public synchronized void methond() {
}
- 修饰静态方法: 锁的 SynchronizedDemo 类的对象
public synchronized static void method() {
}
- 修饰代码块: 明确指定锁哪个对象
- 锁当前对象
public void method() {
synchronized (this) {
}
}
- 锁类对象
public void method() {
synchronized (SynchronizedDemo.class) {
}
}
synchronized的锁机制
-
对象锁:可以将synchronized关键字直接应用于实例方法或实例代码块上。当一个线程进入被synchronized修饰的实例方法或实例代码块时,它会自动获取该对象的内置锁。只有当线程释放锁之后,其他线程才能进入同步块。
-
类锁:可以将synchronized关键字应用于静态方法或类代码块上。当一个线程进入被synchronized修饰的静态方法或类代码块时,它会自动获取该类的Class对象的内置锁。类锁是属于整个类的,对于同一个类的不同实例,他们共享同一个类锁。
-
锁对象:可以使用synchronized关键字加锁指定的对象。通过指定一个对象作为锁,多个线程可以根据这个对象来实现同步。当一个线程进入synchronized代码块时,它会尝试获取指定对象的内置锁,只有当线程释放锁之后,其他线程才能获得锁并执行同步代码。
常见锁策略
乐观锁与悲观锁
悲观锁是在数据被使用前加锁,防止数据被其他线程修改。
乐观锁则是在更新数据时检查数据是否被其他线程修改过,如果没有则更新成功,否则返回失败。
Synchronized 初始使用乐观锁策略,当发现锁竞争比较频繁的时候, 就会自动切换成悲观锁策略。
重量级锁与轻量级锁
轻量级锁是一种优化的锁,它在CAS操作时使用CPU的自旋机制,如果自旋成功则获取到锁,否则进入睡眠状态。
重量级锁是一种传统的锁,它依赖于操作系统的MutexLock(互斥锁)来实现,当有多个线程竞争同一个锁时,会阻塞其他线程等待释放。
公平锁与非公平锁
假设有A,B,C三个线程依次进行同一把锁的获取,线程A获取成功了,线程B与C获取失败。
等待线程A释放锁后,线程B与C,如何获取锁
公平锁策略: 遵守 “先来后到”。B 比 C 先来的。当 A 释放锁的之后,B 就能先于 C 获取到锁。
非公平锁策略:不遵守 “先来后到”。B 和 C 都有可能获取到锁。
synchronized 是非公平锁
可重入锁与不可重入锁
可重入锁的意思就是允许同一个线程多次获取同一把锁。
Java里只要以Reentrant开头命名的锁都是可重入锁,而且JDK提供的所有现成的Lock实现类,包括synchronized关键字锁都是可重入的。
可以理解为一个线程没有释放锁,然后又尝试再次加锁。
按照之前对锁的理解就是,锁没有释放,进行再次加锁就会进行阻塞,直到第一次的锁被释放,才能获取到第二个锁,但释放第一个锁也由该线程来进行,结果现在这个线程啥都干不了,也就只能形成死锁了。
这样的锁称其为不可重入锁。
synchronized 是可重入锁
自旋锁
为防止线程在抢锁失败后进入阻塞状态,经过很久才能再次被调度的情况。
while (!locked.compareAndSet(false, true)) {// 不断循环直到获取到锁}
如果获取锁失败,立即再尝试获取锁, 无限循环,直到获取到锁为止。 第一次获取锁失败, 第二次的尝试会在极短的时间内到来。
缺点:如果锁被其他线程持有的时间比较久, 那么就会持续的消耗 CPU 资源。
synchronized 中的轻量级锁策略大概率就是通过自旋锁的方式实现的
读写锁
一个线程对于数据的访问, 主要存在两种操作: 读数据 和 写数据.
-
两个线程都只是读一个数据, 此时并没有线程安全问题. 直接并发的读取即可.
-
两个线程都要写一个数据, 有线程安全问题.
-
一个线程读另外一个线程写, 也有线程安全问题.
读写锁就是把读操作和写操作区分对待。 Java 标准库提供了ReentrantReadWriteLock 类,实现了读写锁。
-
ReentrantReadWriteLock.ReadLock 类表示一个读锁。这个对象提供了 lock / unlock 方法进行加锁解锁。
-
ReentrantReadWriteLock.WriteLock 类表示一个写锁。 这个对象也提供了 lock / unlock 方法进行加锁解锁
读加锁和读加锁之间, 不互斥.
写加锁和写加锁之间, 互斥.
读加锁和写加锁之间, 互斥
Synchronized 不是读写锁
想了解更多也可以看我的笔记专栏哈哈
相关文章:

Java中synchronized:特性、使用、锁机制与策略简析
目录 synchronized的特性互斥性可见性可重入性 synchronized的使用方法synchronized的锁机制常见锁策略乐观锁与悲观锁重量级锁与轻量级锁公平锁与非公平锁可重入锁与不可重入锁自旋锁读写锁 synchronized的特性 互斥性 synchronized确保同一时间只有一个线程可以进入同步块或…...
记一次clickhouse手动更改分片数异常
背景:clickhouse中之前是1分片1副本,随着数据量增多,想将分片数增多,于是驻场人员手动添加了分片数的节点信息 <clickhouse><!-- 集群配置 --><clickhouse_remote_servers><feihuang_ck_cluster><sha…...

深度学习论文: ISTDU-Net:Infrared Small-Target Detection U-Net及其PyTorch实现
深度学习论文: ISTDU-Net:Infrared Small-Target Detection U-Net及其PyTorch实现 ISTDU-Net:Infrared Small-Target Detection U-Net PDF: https://doi.org/10.1109/LGRS.2022.3141584 PyTorch代码: https://github.com/shanglianlm0525/CvPytorch PyTo…...

图像识别-YOLO V8安装部署-window-CPU-Pycharm
前言 安装过程中发现,YOLO V8一直在更新,现在是2023-9-20的版本,已经和1月份刚发布的不一样了。 eg: 目录已经变了,旧版预测:在ultralytics/yolo/v8/下detect 新版:ultralytics/models/yolo/detect/predict.py 1.安…...
js禁用F1至F12、禁止缩放、取消选中并且取消右键操作、打印、拖拽、鼠标点击弹出自定义信息、禁用开发者工具js
禁用js //禁止缩放 //luwenjie hualun window.addEventListener(mousewheel, function (event) {if (event.ctrlKey true || event.metaKey) {event.preventDefault();} }, {passive: false});//firefox window.addEventListener(DOMMouseScroll, function (event) {if (even…...

Zabbix5.0_介绍_组成架构_以及和prometheus的对比_大数据环境下的监控_网络_软件_设备监控_Zabbix工作笔记
z 这里Zabbix可以实现采集 存储 展示 报警 但是 zabbix自带的,展示 和报警 没那么好看,我们可以用 grafana进行展示,然后我们用一个叫睿象云的来做告警展示, 会更丰富一点. 可以看到 看一下zabbix的介绍. 对zabbix的介绍,这个zabbix比较适合对服务器进行监控 这个是zabbix的…...

百度SEO优化TDK介绍(分析下降原因并总结百度优化SEO策略)
TDK是SEO优化中很重要的部分,包括标题(Title)、描述(Description)和关键词(Keyword),为百度提供网页内容信息。其中标题是最重要的,应尽量突出关键词,同时描述…...

搭建自动化 Web 页面性能检测系统 —— 设计篇
页面性能对于用户体验、用户留存有着重要影响,当页面加载时间过长时,往往会伴随着一部分用户的流失,也会带来一些用户差评。性能的优劣往往是同类产品中胜出的影响因素,也是一个网站口碑的重要评判标准。 一、名称解释 前端监控…...

记一次 mysql 数据库定时备份
环境:Centos 7.9 数据库:mysql 8.0.30 需求:生产环境 mysql 数据(约670MB)备份。其中存在大字段、longblob字段 参考博客:Linux环境下使用crontab实现mysql定时备份 - 知乎 一、数据库备份 1. 备份脚本。创…...

淘宝分布式文件存储系统(一) -TFS
淘宝分布式文件存储系统( 一 ) ->>TFS 目录 : 什么是文件系统文件存储的一些概念文件的结构系统读取文件的方式为什么采用大文件结构的原因 文件系统 : 将我们的数据整合成目录或者文件,提供对文件的存取接口,基于文件的权限进行访问,简单的说,文件系统就是对文件进行…...

LLM各层参数详细分析(以LLaMA为例)
网上大多分析LLM参数的文章都比较粗粒度,对于LLM的精确部署不太友好,在这里记录一下分析LLM参数的过程。 首先看QKV。先上transformer原文 也就是说,当h(heads) 1时,在默认情况下, W i Q W_i…...
linux ansible(三)
ansible 配置详解 3.1 ansible 安装方式 ansible安装常用两种方式,yum安装和pip程序安装 3.1.1 使用 pip(python的包管理模块)安装 需要安装一个python-pip包,安装完成以后,则直接使用pip命令来安装我们的ansible包 …...

Anaconda和Pycharm详细安装 配置教程
Anaconda:是一个开源的Python发行版本,其中包含了conda、Python等180多个科学包及其依赖项。【Anaconda下载】 PyCharm:PyCharm是一种Python IDE,带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具。【PyCharm下载】…...

利用Linux虚拟化技术实现资源隔离和管理
在现代计算机系统中,资源隔离和管理是非常重要的,特别是在多租户环境下。通过利用Linux虚拟化技术,我们可以实现对计算资源(如CPU、内存和存储)的隔离和管理,以提供安全、高效、稳定的计算环境。下面将详细…...

12基于MATLAB的短时傅里叶变换( STFT),连续小波变换( CWT),程序已调通,可以直接运行。
基于MATLAB的短时傅里叶变换( STFT),连续小波变换( CWT),程序已调通,可以直接运行...

k8s使用时无法ping通服务器From IP地址 icmp_seq=1 Destination Host Unreachable
天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…...

两种风格的纯CSS3加载动画
<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>加载动画</title><style>.loader {w…...

Spring Cloud Eureka:服务注册与发现
💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! Spring Cloud Eureka:服务注册与发现 Spring Cloud Eureka是Spring Cloud生态系统中的一个组件,它是用于实现服务注册与发现的服务治理组件。在…...

安防监控视频云存储平台EasyNVR对接EasyNVS时,一直不上线该如何解决?
视频安防监控平台EasyNVR可支持设备通过RTSP/Onvif协议接入,并能对接入的视频流进行处理与多端分发,包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等多种格式。 近期有用户在使用安防视频平台EasyNVR对接上级平台EasyNVS时,出现了一直不上线…...

【完美解决】GitHub连接超时问题 Recv failure: Connection was reset
问题: 已经开了梯子但是在Idea中使用git(GitHub)还是连接超时Recv failure: Connection was reset。此时需要让git走代理。 解决方案: 1.对右下角网络点击右键 -> 打开网络和Internet设置 2.代理 -> 查看到地址和端口号…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果
智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...