Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >synchronized与Lock 擂台之战

synchronized与Lock 擂台之战

作者头像
用户1260737
发布于 2018-01-31 03:24:51
发布于 2018-01-31 03:24:51
7540
举报
文章被收录于专栏:趣谈编程趣谈编程

面试官:说说synchronized和Lock(或ReentrantLock)的区别

Java 1.5之后,对共享变量访问的协调机制除了之前的synchronized和volatile又多了一个Lock,深刻理解synchronized与Lock,并熟悉两者的应用场景对编写并发程序有着非常重要的作用

部落新添大将

话说JDK1.5之前,并发部落 synchronized 和 volatile 可谓红人,无人不知,无人不晓

当多个线程访问同一共享变量时,只需在操作该共享变量的方法上加一个synchronized,就可以保证同一时刻只有一个线程操作该共享变量(还有其他用法)

这使得多个线程按照要求合理的占用和释放资源,所以并发部落如此昌盛,synchronized 功不可没

JDK1.5的到来,打破了这种局面

一个名为Lock的接口出世,听说这个Lock刚出世就神通广大,不仅有着和synchronized一样的功能,在锁的获取上还可以定时获取,轮询获取和可中断获取等等一系列高级的技能

这消息传到了synchronized的耳中,心中很是不爽,决定找个擂台与Lock一决胜负,可是想到Lock有那么多的优势,自己心中顿时没了底气,所以他决定对自己升级一下再去PK

synchronized找到了JDK老大诉苦,说要改造改造自己

JDK老大说道:“之前一直有人抱怨你慢,因为线程要获得锁和释放锁都要进行一次重量级的系统调用,我早都想着给你优化优化”

“好啊好啊”,synchronized说道

“其实我这里已经有方案了,你暂且回去,等JDK1.6的到来吧”JDK回道

“好的”,synchronized回复到。

新synchronized问世

终于JDK1.6到来了,synchronized在锁的获取和释放上有了重大改进,引入了偏向锁、轻量级锁和重量级锁,这次synchronized信心大增,决定去找Lock PK

synchronized 到了 Lock 跟前,说道:“久闻Lock兄神通广大,今日一见,不知神通在何处?”

Lock 一看这家伙是来挑事的,自己也不甘示弱,“神通之处你自然看不出来,用时方显神通”,Lock回应道

synchronized气的咬牙切齿,但这也正和自己心意,“哦,那我想见识见识,明日部落有一场擂台比武,不知Lock兄能否夺得桂冠”

“那是必然”,Lock回应道

“那明日一决雌雄”,synchronized甩下一句就走了

擂台比武

次日,两人都来到了擂台旁,并发部落的人几乎都来了,都想看看synchronized和Lock的好戏

用法PK

synchronized说道:“首先我是一个关键字,我常常被人称为内置锁,我的使用特别简单,如果你想让某一个方法在同一时刻只能由一个线程访问,那么只需要在方法上加上一个synchronized,如下:

这样对变量 i 的修改就线程安全了”

synchronized说道,“对了,更为让人清爽的是,我的加锁和释放锁都是隐式的,不需要程序员们在代码层次上手动的去加锁和释放锁,是不是很优雅?”

听完synchronized的一番自述后,虽说在用法上稍逊synchronized,但是Lock也不甘示弱,说道:

“我是一个接口,可以有无数的子类去实现我,ReentrantLock就是一个

我的使用也很简单呀,当你想给某一段代码加锁的话,只需要在代码块之前调用 lock() 方法,在之后调用 unlock() 不信你看”:

虽说加锁和释放锁都要在代码层次上显示的去操作,稍复杂一些,不是很优雅,但是我的锁获取和释放更加灵活

比如说有一个先获取锁A,再获取锁B,获取锁B之后释放锁A,然后再释放锁B的需求,我想什么时候获取锁和释放锁,直接调用 lock()unlock() 就OK了

Lock拿出自己的优势来弥补了一下自己的不足

性能PK

早有准备的synchronized暗自窃喜,在这方面现在自己不比Lock差,synchronized说道,“性能这块我现在引入了偏向锁,轻量级锁和重量级锁,这些改变我的性能比之前提高了许多”

“提高了许多现在才和我差不多”Lock 插了一刀,气的synchronized无话可说

用途PK

synchronized这块很是心虚,论用途,Lock比自己多,但是自己还是心理给自己打气,说道:

“并发控制这块的需求,基本上我都可以解决,而且用法很优雅,许多程序员已经习惯了我的存在,并且在发生异常的时候,JVM老大会自动释放锁,这样就避免了死锁的产生”

synchronized抓住Lock一定要调用 unlock() 方法释放锁的缺点不放

“不像有些人,如果不主动 调用 unlock() 释放锁,就很可能造成死锁”,synchronized又补了一句

Lock立马回应道:

“我虽有些许不足,但是我的高级功能很强大,synchronized可以实现的功能我都可以实现,除此之外,我还有 等待可中断可实现公平锁以及锁可以绑定多个条件等高级功能”

台下的观众眼中放光,特别想听听这些都是什么东西,之前都没见过

只见Lock清了清嗓门,一个一个的解释起来了

① 所谓等待可中断就是一个线程去获得一个锁的时候,由于很长时间没有获取到锁,可以放弃等待,处理其他事情

比如有两个线程 A 和 B,A获得了锁,B想获得该锁,那么B线程就挂起了

如果A迟迟不肯放锁,当该锁的获取是可以响应中断(调用lock.lockInterruptibly()),那么当其他线程中断该线程的时候,则B线程就可以响应中断,去做其他事情了

//在获取锁时被中断,抛出 InterruptedException

lock.lockInterruptibly()

如下图:

run() 方法是 B 线程(继承了Thread)的方法,如果不想让B线程等待,只需要让其他线程调用 B 线程的 interrupt() 方法,就会进入到 catch 并执行下面的任务

不必要在read函数的获得锁上一直阻塞等待(一直阻塞等待是因为B线程它一直想获得A线程的所持有的锁,但是迟迟没有得到)

②可实现公平锁:所谓公平锁,就是在多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁

比如A先来获得锁,获取失败,阻塞等待,然后B来获取锁,获取失败,阻塞等待,最后当这个锁释放的时候,按照公平锁的原则,A肯定会得到锁在ReentrantLock的构造函数中,可以传入是否为公平锁的参数,如:

// 使用公平锁机制

Lock lock = new ReentrantLock(true);

③ 所谓 锁绑定多个条件是指,一个 ReentrantLock 对象可以绑定多个 Condition对象(通过 lock.newCondition 创建一个Condition)

比如现在有两个方法methodA和methodB,有两个线程阻塞在methodA上,有一个线程阻塞在methodB上,现在想单独唤醒methodA上的线程 或者单独唤醒阻塞在methodB上的线程

如果是wait()和notify或notifyAll() 机制,则需要两个锁而ReentrantLock只需要一把锁就可以完成,一把锁可以new 多个Condition

在调用 lock.lock() 后 可以调用 Condition 的 await() 方法使线程处于等待状态,释放锁,如下:

当有两个线程进入methodA时,一个获得锁[ lock.lock() ]后又释放锁[ conditionA.await() ],进入等待状态

另一个线程同样也进入等待状态,当其他线程调用 conditionA.signalAll() [也可调用 conditionA.signal() ] 的时候,可以唤醒在conditionA上等待的所有线程:如下

这样,在conditionA上等待的线程就被全部唤醒了,这和 notifyAll() 的作用一样,只是这里的condition 可以由一个 lock 创建很多

同样的也可以在methodB中使用 conditionB, 最后用 conditionB.signalAll() 来唤醒在 conditionB 上等待的所有线程

如果是wait 和 notify 的话,就需要多个锁了

听完Lock的自述后,大家都赞不绝口,地下一片掌声

最后的胜负

这时候裁判大人出场了,听完两位大侠的叙述,我看两人各有优缺

synchronized 为许多开发人员所熟悉,并且简洁紧凑,许多现有的程序都已经使用了synchronized

而 Lock 有许多高级功能,在一些特定的场合能派上大用处,但是Lock的危险性很高,如果忘记在finally块中调用 unlock,那么就埋下了一颗定时炸弹

所以只有当synchronized 不能满足需求时,才可以使用ReentrantLock

现在我宣布,synchronized 略胜一筹,但Lock也是很不错的

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-10-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 趣谈编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
58面试官居然问我Synchronized底层实现,锁升级的具体过程?
这是我去年7,8月份面58被问的一个面试题,说实话被问到这个问题还是很意外的,感觉这个东西没啥用啊,直到后面被问了一波new Object,Integer对象等作为加锁对象行吗?会出现哪些问题?为啥java6后synchronized性能大幅上升?我彻底蒙蔽了。下面详细总结一下
Java识堂
2020/02/19
8041
谈谈 synchronized 和 ReentrantLock 的区别
“可重入锁”概念是:自己可以再次获取自己的内部锁。比如,一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁时,还可以再获取的;如果不可锁重入的话,就会造成死锁;同一个线程每次获取锁,锁的计数器都自增1,所以要等到锁的计数器下降为0时,才能最终释放锁。
happyJared
2019/07/08
2.5K0
多线程编程学习四(Lock 的使用)
一、前言     本文要介绍使用Java5中 Lock 对象,同样也能实现同步的效果,而且在使用上更加方便、灵活,主要包括 ReentrantLock 类的使用和ReentrantReadWriteLock 类的使用。 二、使用ReentrantLock 类 1、在java多线程中,可以使用synchronized关键字来实现线程之间同步互斥,但在JDK1.5中新增加的ReentrantLock也能达到同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定、多路分支通知等功能,而且在使用上也比synchr
JMCui
2018/03/16
7710
多线程编程学习四(Lock 的使用)
线程安全和锁机制(三)synchronized和Lock
synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性
提莫队长
2021/03/03
4620
深入理解java并发锁
确保线程安全最常见的做法是利用锁机制(Lock、sychronized)来对共享数据做互斥同步,这样在同一个时刻,只有一个线程可以执行某个方法或者某个代码块,那么操作必然是原子性的,线程安全的。
Java宝典
2021/01/14
4430
深入理解java并发锁
并发编程之synchronized VS ReentrantLock
一、相似点 这两种同步方式有很多相似之处,它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的(操作系统需要在用户态与内核态之间来回切换,代价很高,不过可以通过对锁优化进行改善)。 二、区别 1、区别 1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现; 2)synchronized在发生异常时,会自动释放线程占有的锁,
lyb-geek
2018/03/27
8290
Java多线程学习(六)Lock锁的使用
我自己总结的Java学习的系统知识点以及面试问题,目前已经开源,会一直完善下去,欢迎建议和指导欢迎Star: https://github.com/Snailclimb/Java-Guide
用户2164320
2018/06/23
11.5K0
Java多线程学习(六)Lock锁的使用
Lock介绍
java.util.concurrent.locks包下常用的类与接口(lock是jdk 1.5后新增的)。
HLee
2021/10/09
8510
Lock介绍
[面试总结篇] Synchronized 和 Lock 的区别
1. synchronized是一个关键字而lock是一个接口(lock、lockInterruptibly、tryLock、unlock、newCondition)。
BUG弄潮儿
2022/12/05
7070
这一次彻底搞懂Java的Lock接口到底有什么用!
这俩问题,管程都能一把梭。JUC是通过Lock、Condition接口实现的管程:
JavaEdge
2021/04/28
4910
这一次彻底搞懂Java的Lock接口到底有什么用!
聊一聊 ReentrantLock 类的一些玩法
在上一篇文章中,我们介绍了ReentrantLock类的一些基本用法,今天我们重点来介绍一下ReentrantLock其它的常用方法,以便对ReentrantLock类的使用有更深入的理解。
Java极客技术
2023/10/23
2200
聊一聊 ReentrantLock 类的一些玩法
Java多线程学习(六)——Lock的使用
锁是用于通过多个线程控制对共享资源的访问的工具。通常,锁提供对共享资源的独占访问:一次只能有一个线程可以获取锁,并且对共享资源的所有访问都要求首先获取锁。 但是,一些锁可能允许并发访问共享资源,如ReadWriteLock的读写锁。Java5之后并发包中新增了Lock接口以及相关实现类来实现锁功能。
小森啦啦啦
2019/09/17
5010
面试官:谈一谈java中基于AQS的并发锁原理
我:java中的AQS是指AbstractQueuedSynchronizer类,java中并发锁控制逻辑都是基于这个类来实现的。
jinjunzhu
2020/08/20
1.5K0
锁:Sychronized、Lock
锁是用来在多线程并发阶段保障数据同步的重要手段,防止出现脏数据,加锁代码在某个时间点只能由一个线程运行,其他线程等待。
Liusy
2020/09/01
4350
锁:Sychronized、Lock
一文看懂JUC之AQS机制
为了解决原子性的问题,Java加入了锁机制,同时保证了可见性和顺序性。JDK1.5的并发包中新增了Lock接口以及相关实现类来实现锁功能,比synchronized更加灵活,开发者可根据实际的场景选择相应的实现类。
Java小咖秀
2021/06/08
4770
一文看懂JUC之AQS机制
【JUC基础】05. Synchronized和ReentrantLock
前面两篇中分别讲了Synchronized和ReentrantLock。两种方式都能实现同步锁,且也都能解决多线程的并发问题。那么这两个有什么区别呢? 这个也是一个高频的面经题。
有一只柴犬
2024/01/25
1730
【JUC基础】05. Synchronized和ReentrantLock
Java并发编程--Lock
  Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。synchronized方法或代码块的使用提供了对与每个对象相关的隐式监视器锁的访问,但却强制所有锁获取和释放均要出现在一个块结构中:当获取了多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的词法范围内释放所有锁。Lock 实现提供了使用 synchronized 方法和语句所没有的其他功能,包括提供了一个非块结构的获取锁尝试 (tryLock())、一个获取可中断锁的尝试 (lockInterruptibly()) 和一个获取超时失效锁的尝试 (tryLock(long, TimeUnit))。
在周末
2019/09/11
4700
【高并发】面试官:Java中提供了synchronized,为什么还要提供Lock呢?
作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架地址:
冰河
2020/10/29
5240
【高并发】面试官:Java中提供了synchronized,为什么还要提供Lock呢?
Java多线程系列——Lock锁
Java多线系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线程的深入剖析。
说故事的五公子
2019/12/10
1.4K0
Java多线程系列——Lock锁
Java并发编程(六)---lock
前面几篇文章,我们学习了synchronized的相关知识,以及死锁的发生条件以及避免的方式,其中有一种破坏死锁的方式就是破坏不可抢占条件,通过synchronzied不能实现的,因为synchronized在申请资源的时候,如果申请不到就只能进入阻塞状态,啥都干不了,也不能中断。所以只能通过本期的主角lock 来处理。
码农飞哥
2021/08/18
2870
相关推荐
58面试官居然问我Synchronized底层实现,锁升级的具体过程?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档