锁的策略及synchronized详解

加锁过程中,处理冲突的过程中,涉及到的一些不同的处理方式。锁的策略决定了线程如何获取和释放锁以及在何种情况下阻塞和唤醒线程。

1. 常见的锁策略 

1.1  乐观锁和悲观锁

  • 乐观锁:在加锁之前,预估当前出现锁冲突的概率不大,因此在进行加锁的时候不会做太多的工作,于是加锁的速度就可能更快,当更容易引入一些其他的问题,例如肯能会消耗更多的CPU资源。
  • 悲观锁:在加锁之前,预估当前出现锁冲突的概率比较大,因此在加锁的时候就会做更多的工作,所以加锁的速度可能会更慢,但是整个过程中不容易出现其他问题。

1.2 轻量级锁和重量级锁

  • 轻量级锁:加锁的开销更小,加锁速度更快。轻量级锁一般就是乐观锁
  • 重量级锁:加锁的开销更大,加锁速度更慢。重量级锁一般也就是悲观锁

注意:

  • 轻量级锁和重量级锁的定义是加锁后对结果的评价而来
  • 乐观锁和悲观锁的定义是对未发生的事做出预估而来 

 1.3 自旋锁和挂起等待锁

  • 自旋锁:自旋锁就是轻量级锁的一种典型实现,进行加锁的时候搭配一个while循环,如果加锁成功则循环结束,加锁失败不进入阻塞而是再次尝试。这个反复快速执行的过程就称为“自旋”。一旦其他线程释放了锁能第一时间拿到,同时这样的自旋锁也是乐观锁,使用自旋的前提就是预期冲突概率不大,其他线程释放了锁,就能第一时间拿到。如果当前加锁的线程特别多,使用自旋锁则会浪费过多的CPU资源。
  • 挂起等待锁:挂起等待锁就是悲观锁的一种实现,同时也是重量级锁,加锁失败时进入阻塞等待,不会继续消耗CPU资源,挂起等待的时候需要内核调度器介入,这部分要完成的操作很多,所以真正获取到锁的时间会更长,适用于锁冲突激烈的情况。

1.4 普通互斥锁和读写锁 

  • 普通互斥锁:普通互斥锁也被称为排它锁,它确保在任何时刻只有一个线程可以获得锁并访问共享资源。当一个线程持有锁时,其他线程需要等待锁的释放才能继续执行,类似于synchronized 加锁解锁。
  • 读写锁:读写锁把锁分为两种情况,加读锁和加写锁。读锁和读锁之间,不会出现锁冲突,即允许多个线程同时读取共享资源;写锁和写锁之间会出现锁冲突;读锁和写锁之间会出现锁冲突。

 为什么要引入读写锁?

如果两个线程读同一个数据,这个操作本身就是线程安全的,不需要阻塞,如果使用 synchronized 加锁,则会阻塞,对于性能有一定影响。

1.5 公平锁和非公平锁 

  • 公平锁:公平锁按照请求锁的顺序分配锁资源即 “先来后到” ,保证每个线程都有公平的机会获得锁。这种策略避免了线程饥饿现象,但会导致额外的开销,因为线程可能需要等待其他线程释放锁。
  • 非公平锁:不遵守 “先来后到” 的规则,所有线程都有可能获取到锁。

1.6 可重入锁和不可重入锁 

  •  可重入锁:一个线程使用一把锁连续加锁两次,不会产生死锁,就是可重入锁,如 synchronized。
  • 不可重入锁:一个线程使用一把锁连续加锁两次,会产生死锁,则是不可重入锁。

 

2. synchronized

2.1 synchronized 的锁策略

synchronized 具有自适应能力

synchronized 在某些情况下是 乐观锁/轻量级锁/自旋锁 有些情况下是 悲观锁/重量级锁/挂起等待锁。

内部会自动评估当前锁冲突的激烈程度。

  • 如果当前锁冲突的激烈程度小,就处于 乐观锁/轻量级锁/自旋锁。
  • 如果当前锁冲突的激烈程度大,就处于 悲观锁/重量级锁/挂起等待锁。

2.2 锁升级

当线程执行到 synchronized 的时候,如果锁还是空闲的,就会经历以下过程:

  1. 偏向锁阶段:每个锁对象中有一个偏向锁标记,当这个锁对象首次被加锁时,会进入偏向锁,锁对象会记录下该线程的id,如果下次加锁,没有锁竞争,并且仍然是同一个线程拿到锁,这个锁仍然是偏向锁。偏向锁并不会真的加上锁,由于两次都是同一个线程来获取锁,所以认为没有锁竞争,所以就不用真的加上锁,免去了一定开销。
    注意:如果一个锁此前都是线程1的偏向锁,在某次线程1再次尝试加锁时,出现了一个线程2 也尝试加锁,此时这个锁会升级为轻量级锁,然后再由这两个线程来竞争。但是本次加锁一定是线程1拿到锁,线程2下次竞争才可能拿到锁。
     
  2. 轻量级锁阶段:当偏向锁出现锁竞争时,或者本次加锁的线程与第一次记录的不同,这个锁则会升级为轻量级锁。此处是通过自旋锁的方式来实现的。
     
  3. 重量级锁阶段:当参与锁竞争的线程达到某个阈值,就会从轻量级锁升级到重量级锁。

注意:锁的升级不可逆。

2.3 锁消除 

锁消除也是 synchronized 中内置的优化策略。

 编译器编译代码的时候,如果发现这个代码不需要加锁,就会取消掉这个锁。但是这个优化是比较有限的,如果代码稍微复杂一些,编译器是判断不了是否需要加锁的。

2.4 锁粗化 

会把多个加锁代码块合成一个代码块,去除了多次加锁解锁的开销。

例如:

synchronized(locker) {a++;
}
synchronized(locker) {b++;
}

可粗化为:

synchronized(locker) {a++;b++;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/209638.shtml

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

相关文章

Docker三 | 数据卷

目录 Docker数据卷简介 添加数据卷的命令 容器数据卷的继承 Docker数据卷简介 Docker容器产生的数据,如果不备份,当容器实例删除后,容器中的数据也会消失,为了保存数据可以在Docker中使用数据卷。Docker数据卷是宿主机的一个可以…

vue3中子组件调用父组件的方法

<script lang"ts" setup>前提 父组件&#xff1a; 子组件&#xff1a; const emit defineEmits([closeson]) 在子组件的方法中使用&#xff1a; emit(closeson)

EP15:动态内存管理概述(c语言)malloc,calloc,realloc函数的介绍使用及柔性数组的介绍

如果学习方向是c方向那么c语言有三个板块的知识是非常重要的. 1:指针 2:结构体 3;动态内存管理. 序言:在c语言中,什么是动态内存 C语言中的动态内存是指在程序运行时&#xff0c;根据需要动态地分配内存空间的一种内存管理方式。与静态内存相比&#xff0c;动态内存的大小和生…

12.ROS导航模块:gmapping、AMCL、map_server、move_base案例

目录 1 导航概述 2 导航简介 2.1 导航模块简介 1.全局地图 2.自身定位 3.路径规划 4.运动控制 5.环境感知 2.2 导航坐标系odom、map 1.简介 2.特点 3.坐标系变换 2.3 导航条件说明 1.硬件 2.软件 3 导航实现 3.1 创建本篇博客的功能包 3.2 建图--gmapping 3.…

JavaScript基础知识整理(最全知识点, 精简版,0基础版)

文章目录 一、输入和输出内容 1.1 输出 1.1.1 在浏览器的控制台输出打印 1.1.2 直接在浏览器的页面上输出内容 1.1.3 页面弹出警告对话框 1.2 输入 二、变量 2.1 变量是什么 2.2 变量的声明和赋值 2.3 变量的命名规范和规范 三、变量扩展&#xff08;数组&#xff09; 3.1 数组…

Cypress:前端自动化测试的终极利器

引言&#xff1a; 在现代软件开发中&#xff0c;前端自动化测试已经成为了一个不可或缺的环节。它不仅可以提高开发效率&#xff0c;减少手动测试的工作量&#xff0c;还可以保证软件的稳定性和质量。而在众多的前端自动化测试工具中&#xff0c;Cypress无疑是其中的佼佼者。本…

openGauss学习笔记-144 openGauss 数据库运维-例行维护-慢sql诊断

文章目录 openGauss学习笔记-144 openGauss 数据库运维-例行维护-慢sql诊断144.1 背景信息144.2 前提条件 openGauss学习笔记-144 openGauss 数据库运维-例行维护-慢sql诊断 144.1 背景信息 在SQL语句执行性能不符合预期时&#xff0c;可以查看SQL语句执行信息&#xff0c;便…

文章解读与仿真程序复现思路——中国电机工程学报EI\CSCD\北大核心《考虑垃圾处理与调峰需求的可持续化城市多能源系统规划》

这个标题涵盖了城市多能源系统规划中的两个重要方面&#xff1a;垃圾处理和调峰需求&#xff0c;并强调了规划的可持续性。 考虑垃圾处理&#xff1a; 含义&#xff1a; 垃圾处理指的是城市废弃物的管理和处置。这可能涉及到废物分类、回收利用、焚烧或填埋等方法。重要性&…

GIS入门,Leaflet介绍,Leaflet可以做什么,网页中如何使用Leaflet地图,vue中如何使用Leaflet地图

VueLeafLet教程推荐&#xff1a;《VueLeaflet入门》 Leaflet介绍 Leaflet是一个开源的JavaScript库&#xff0c;用于创建交互式的地图和地图应用。Leaflet框架具有轻量级、灵活性强、易于使用和扩展等特点&#xff0c;支持各种地图服务商&#xff08;如OpenStreetMap、Google…

前端知识笔记(三十八)———HTTPS:保护网络通信安全的关键

当谈到网络通信和数据传输时&#xff0c;安全性是一个至关重要的问题。在互联网上&#xff0c;有许多敏感信息需要通过网络进行传输&#xff0c;例如个人身份信息、银行账户信息和商业机密等。为了保护这些信息不被未经授权的人访问和篡改&#xff0c;HTTPS&#xff08;超文本传…

【开源】基于Vue+SpringBoot的河南软件客服系统

文末获取源码&#xff0c;项目编号&#xff1a; S 067 。 \color{red}{文末获取源码&#xff0c;项目编号&#xff1a;S067。} 文末获取源码&#xff0c;项目编号&#xff1a;S067。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统管理人员2.2 业务操作人员 三、…

搞懂内存函数

引言 本文介绍memcpy的使用和模拟实现、memmove的使用和模拟实现、memcmp使用、memset使用 ✨ 猪巴戒&#xff1a;个人主页✨ 所属专栏&#xff1a;《C语言进阶》 &#x1f388;跟着猪巴戒&#xff0c;一起学习C语言&#x1f388; 目录 引言 memcpy memcpy的使用 memcpy的…

JS加密/解密之HOOK实战2

上一篇文章介绍了HOOK常规的应用场景&#xff0c;这篇我们讲一下HOOK其他原生函数。又是一个新的其他思路 很多时候&#xff0c;当我们想要某些网站的请求参数的时候&#xff0c;因为某些加密导致了获取起来很复杂。 这时候hook就十分方便了 源代码 var _JSON_Parse JSON.…

scp 指令详细介绍

目录 1. 基本语法 2. 例子 从本地到远程 从远程到本地 从远程到远程 使用端口和指定私钥 递归复制目录 3. 注意事项 如何拷贝文件的软链接 SCP&#xff08;Secure Copy Protocol&#xff09;是一种用于在计算机之间安全地传输文件的协议。它通过加密的方式在网络上安全…

Vue:Vue的开发者工具不显示Vue实例中的data数据

一、情况描述 代码&#xff1a; 页面&#xff1a; 可以看到&#xff0c;input获取到了data数据&#xff0c;但是&#xff0c;vue-devtool没有获取到data数据 二、解决办法 解决办法1&#xff1a; data.name的值不能全是中文&#xff0c;比如改成aa尚硅谷 解决办法2&…

C语言 编程题

C语言学习&#xff01; 1.小明上课需要走n阶台阶&#xff0c;他每次可以选择走一阶或者走两阶&#xff0c;他一共有多少种走法&#xff1f; 输入描述&#xff1a;输入包含一个整数n&#xff08;1 ≤ n ≤30&#xff09; 输出描述&#xff1a;输出一个整数&#xff0c;即小明可…

LeetCode 1457. 二叉树中的伪回文路径||位运算 DFS

1457. 二叉树中的伪回文路径 给你一棵二叉树&#xff0c;每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「伪回文」的&#xff0c;当它满足&#xff1a;路径经过的所有节点值的排列中&#xff0c;存在一个回文序列。 请你返回从根到叶子节点的所有路径中 伪回文 路径的…

Golang优雅实现按比例切分流量

我们在进行灰度发布时&#xff0c;往往需要转发一部分流量到新上线的服务上&#xff0c;进行小规模的验证&#xff0c;随着功能的不断完善&#xff0c;我们也会逐渐增加转发的流量&#xff0c;这就需要按比例去切分流量&#xff0c;那么如何实现流量切分呢&#xff1f; 我们很容…

力扣(LeetCode)-1. 两数之和

给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回…

【交流】PHP生成唯一邀请码

目录 前言&#xff1a; 1.随机生成&#xff0c;核对user表是否已存在 代码&#xff1a; 解析&#xff1a; 缺点&#xff1a; 2.建表建库&#xff0c;每次从表中随机抽取一条&#xff0c;用完时扩充 表结构 表视图 代码 解析 缺点 结论&#xff1a; 前言&#xff1a; …