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

【锁】聊一聊ReentrantLock 和 Synchronized 的区别

今天我们来聊一聊 ReentrantLock 和 Synchronized 的区别!

如何使用

Synchronized 可以修饰实例方法,静态方法,代码块。自动释放锁。

ReentrantLock 一般需要 try catch finally语句,在 try中 获取锁,在 finally释放锁。需要手动释放锁。

实现原理

Synchronized 是重量级锁。重量级锁需要将线程从内核态和用户态来回切换。如:A线程切换到B线程,A线程需要保存当前现场,B线程切换也需要保存现场。这样做的缺点是耗费系统资源。

ReentrantLock 是轻量级锁。采用 cas+volatile 管理线程,不需要线程切换切换,获取锁线程觉得自己肯定能成功,这是一种乐观的思想(可能失败)。

用一个栗子来说明:
比如大家在看我这篇文章时,觉得对“重量级锁”这个概念不是很明白,就会马上去百度什么是“重量级锁”,之后回来再继续往下面看文章,对于这种行为我们称之为切换。保存现场的意思就是你的大脑需要记住你跳跃的点然后继续阅读,如果文章篇幅大,你的大脑可能需要记忆越多的东西,会越耗费脑神经。同理,在轻量级锁中,你觉得“重量级锁”概念不是很明白,他不会立刻去翻看其他文章,他会坚持会儿继续看,如果实在不明白再去翻资料了。需要注意的是:这是两种不一样的思维方式,前者是被动阻塞悲观锁,状态是 block,后者是主动的阻塞乐观锁,状态是wait。

公平和非公平

Synchronized 只有非公平锁。

ReentrantLock 提供公平和非公平两种锁,默认是非公平的。公平锁通过构造函数传递 true 表示。

用一个生活中的例子来说明:排队买票,Synchronized 允许插队,如果 ReentrantLock 是公平锁,就不许插队了。

可重入锁

Synchronized 和 ReentrantLock 都是可重入的,Synchronized 是本地方法由 C++ 实现,而 ReentrantLock 是 JUC 包中的,用 Java 实现。

用一个例子来说明:进入别墅的时候,房里外各有一把锁,但只有唯一的钥匙可以开,拥有钥匙的人可以先进入门1,再进入门2,其中进入门2就称之为锁可重入。

在 ReentrantLock 中,重入次数用整型 state 表示。进入 1 次递增 1 次,出来 1 次递减 1 次。

可中断的

Synchronized 是不可中断的。

ReentrantLock 提供可中断和不可中断两种方式。其中 lockInterruptibly方法表示可中断,lock 方法表示不可中断。

不可中断(Synchronized/lock):
如果你排在队伍的前面,而电影票售卖窗口正在忙着服务前面的人,即使有人急需通过,也只能等,直到当前服务的顾客完成交易。你不能跳过当前的人,必须等队伍前的每一个人处理完。

例子: 你排队买票,虽然有人让你先去,但如果你不主动走到窗口,你不能插队。

可中断(lockInterruptibly):
在排队时,假设你可以设置一个提醒,一旦有人在等待时给你发信号,你就可以决定放弃排队,转而做其他事情。你不再强制等待,能更灵活地应对情况。

例子: 你排队时,朋友如果突然需要帮忙,你可以选择离开队伍,避免浪费时间,去帮忙再回来排队。

条件队列

Synchronized 只有一个等待队列。

ReentrantLock 中一把锁可以对应多个条件队列。通过 newCondition 表示。

Synchronized(一个等待队列)
synchronized 是一种较为简单的同步机制,它有一个全局的等待队列来管理所有被阻塞的线程。如果一个线程因获取锁而被阻塞,其他线程只能按顺序排队等候,直到锁被释放。所有线程共享一个队列,也就是每次只有一个线程能获取锁并进入临界区。

例子:
想象一下,你和朋友们正在排队等着进入一个餐馆。餐馆只有一位服务员(相当于一个锁),每次只能接待一个客人。如果服务员正在为某个顾客提供服务,那么其他顾客只能在外面等候。

你们站在一个排队的队伍里,只有当服务员空闲时,下一位顾客才能进入。所有顾客共用这一个队伍,没有区分你们想要不同的服务(比如,有的想吃甜点,有的想喝水)。
队列的特点:无论你想要什么服务,大家都排在一个共同的队列中。

ReentrantLock(多个等待队列)
ReentrantLock 提供了更细粒度的控制,它不仅支持多个线程等待锁的情况,还允许每个线程根据不同的需求或条件等待。通过 newCondition() 方法,你可以为每种不同的条件创建一个等待队列。例如,你可以根据线程请求的特定条件(比如需要“喝水”或“吃甜点”)来分别管理队列。

例子:
假设你和朋友们一起去餐馆,餐馆有一个服务员,但它不止一个队伍,而是分成了不同的队伍。例如,有一队等着点甜点的顾客(队伍A),另一队等着喝水的顾客(队伍B),每队顾客根据自己的需求排队等候服务。

队伍A(想吃甜点的顾客)和 队伍B(想喝水的顾客)是不同的队伍。如果服务员正在为 队伍A 的顾客提供服务,那么 队伍B 的顾客不需要等待,服务员可以在完成队伍A的顾客后直接服务队伍B的顾客。
你可以根据需要分别管理不同的队列,这样当一个条件满足时,只会唤醒相关队伍的线程,而不会影响其他队伍的线程。


http://www.mrgr.cn/news/71170.html

相关文章:

  • Vivado+Vscode联合打造verilog环境
  • day12:版本控制器
  • Go常见框架对比
  • 【贪心算法】贪心算法三
  • STM32WB55RG开发(2)----STM32CubeProgrammer烧录
  • 在线项目管理系统有哪些选择?2024年9款推荐
  • 丹摩征文活动|CogVideoX-2b:从安装到上线,轻松搞定全过程!
  • QML —— 圆形波浪进度条控件(附上源码)
  • docker save 和 docker load介绍
  • 常用的8款电脑加密软件分享|电脑办公文件加密软件推荐!
  • 【软考】系统架构设计师-计算机系统基础(1):计算机硬件
  • Linux的进程,线程;FreeRTOS的任务
  • 错误:No bean named ‘cxf‘ is defined
  • 酷炫的鼠标移入效果(附源码!!)
  • 【Spring】Spring框架中有有哪些常见的设计模式
  • 磁集成技术给磁性材料带来哪些新要求?
  • 壁纸集 1.2.12 | 壁纸聚合软件,内置4个图片接口,超多高清壁纸
  • WTN6040FP-14S语音芯片在电梯控制板中的应用开发方案-实现楼层指引背景音乐播放功能
  • Uniapp+Vue3+Ts+Unocss实现小程序、APP、H5的大转盘抽奖效果
  • 通过注解控制是否打印日志
  • ubuntu系统没有/var/log/messages日志文件解决方法
  • vue2和vue3的原理上的区别
  • 马尔科夫链蒙特卡罗 MCMC
  • java对接微信公众号API,实现扫码关注公众号,触发多条消息回复
  • 丹摩征文活动 | 丹摩智算:大数据治理的智慧引擎与实践探索
  • UE4 Cook 从UAT传递参数给UE4Editor