专栏首页趣谈编程synchronized与Lock 擂台之战

synchronized与Lock 擂台之战

面试官:说说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也是很不错的

本文分享自微信公众号 - 趣谈编程(gh_51e791ad9174),作者:涛声依旧

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-10-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 小白科普:线程和线程池

    第一个问题问题就是为什么要多线程啊, 我看了操作系统中的多进程管理,不是挺好的吗? 多线程似乎没有必要啊!

    用户1260737
  • 神奇的ThreadLocal

    慧能:哈哈,你的时间你自己要管理好,我们程序中有时也会记录下某一个函数或者某一段程序所花费的时间

    用户1260737
  • 什么是线程安全,你真的了解吗?

    记得今年3月份刚来杭州面试的时候,有一家公司的技术总监问了我这样一个问题,说你给我说说有哪些线程安全的类,我心里一想,呵呵,这我早都背好了,稀里哗啦说了一大堆,...

    用户1260737
  • 并发编程之synchronized VS ReentrantLock

    一、相似点 这两种同步方式有很多相似之处,它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都...

    lyb-geek
  • Java锁机制了解一下

    Java3y
  • 简述 synchronized 和 java.util.concurrent.locks.Lock 的异同 ?

    主要不同点:Lock 有比 synchronized 更精确的线程语义和更好的性能。

    MickyInvQ
  • 浅谈web自适应

    随着移动设备的普及,移动web在前端工程师们的工作中占有越来越重要的位置。移动设备更新速度频繁,手机厂商繁多,导致的问题是每一台机器的屏幕宽度和分辨率不一样。这...

    疯狂的技术宅
  • 业界 | 谷歌的神经翻译系统并不意味着机器翻译到头了

    AI科技评论按:本文是清华大学教授、中国中文信息学会副理事长孙茂松在MIFS 2016上的演讲实录,AI科技评论编辑整理。此次在北京举办的MIFS 2016是由...

    AI科技评论
  • 独家科普:谷歌“汉译英”错误率降低60%是怎样算出来的?

    两天前谷歌宣布发布新一代神经网路机器翻译系统(Google Neural Machine Translation),简称GNMT,因为使用当前最先进的训练技术,...

    AI科技评论
  • 技术界与翻译界的交锋:机器翻译离我们还有多远? | 清华AI Time

    作为自然语言处理中一项非常重要的应用,现代意义上的机器翻译概念从上世纪40年代提出至今,经过了几代革新,现已初步实现了多场景的落地和应用。而近几年随着机器翻译质...

    大数据文摘

扫码关注云+社区

领取腾讯云代金券