Synchronized与Lock区别,ReentLock源码解析

Synchronized 和Lock 的区别

Synchronized 是java 关键字,实现级别是JVM 级别;Lock 是一个接口,下面有各种实现类;
Synchronized 在方法的实现和 代码块的实现是不一样的,前者是在对象头加一个sync 标志,后者是用monitor_enter 和monitor_exit实现;
Synchronized 是阻塞,可重入,非公平锁;Lock 可重入,公平非公平实现都有;
Lock 在锁获取时可以自己决定是否阻塞,lock 与 try lock;
锁的释放:Synchronized 是由jvm 自动释放,方法执行完毕和线程发生异常;
lock 是需要由程序释放,否则容易造成死锁;

Synchronized 实现

Synchronized同时保证了可见性与原子性

  • Synchronized的使用方法

Synchronized 的使用主要有三种:

  1. 同步代码块(同步某一对象)
    2.同步普通方法(同一类的不同对象持有不同的锁,只有同一对象的方法调用才会同步)
    3.同步静态方法(实际上相当于对类的同步,不管是不是该类的同一对象 都需要同步)
  • Synchronized 原理

Synchronized是java 关键字,根据反编译结果:
对于代码块的Synchronized是通过monitorenter 和monitorexit实现的,关于其可重入的实现类似后面讲到的AQS中的state,进入+1,退出-1,monitor计数为0 代表可获取锁状态;
对于方法上Synchronized的实现,会对该方法增加ACC_SYNCHRONIZED 标识符,碰到该标识符表示访问时也需先获取monitor,本质是一样的,不过是一种隐式的实现。


Lock接口 ReentrantLock 实现:

ReentrantLock 继承自 Lock接口,Lock接口定义了如下几种方法:


lock接口方法

ReentrantLock的实现都是在其抽奖静态内部类Sync 中完成的,ReentLock本身持有Sync对象,而Sync 有两个实现类FairSync 和NonFairSync ;ReentrantLock 默认实现是NonFairSync,因其无参构造方法返回的是NonFairSync对象。Sync由继承AbstractQueuedSynchronizer 也就是传说中的AQS

  • NonFairLock的实现

1. lock()方法实现

lock实现

NonFairLock 的lock方法进来后会直接调用compareAndSetState方法获取锁,获取锁成功就会把当前线程设为锁的持有者(设为AOS[AbstractOwnableSynchronizer]中的独占线程),compareAndSetState是调用unsafe的cas方法,改变AQS中的state值。


compareAndSetState

获取锁失败就会进入acquire()方法:


acquire

首先通过tryAcquire尝试再次获得锁:
nonfair的tryAcquire实现

该方法中会先获得当前线程和当前锁状态,如果c==0,代表现在没人占有锁,直接通过compareAndSetState 进行锁抢占;如果c!=0 则判断当前持有锁的线程是否为当前线程,如果是的话state值增加所需获得锁的次数,并获得锁,这里是可重入锁的实现部分;上述都不满足条件返回false,调用acquireQueued方法,先调用addWaiter:


nonFairAddWaiter实现

addWaiter方法为当前线程生成一个Node 放到等待队列尾部。


acquireQueued

获取前置Node 如果前置节点是队列头head,则说明当前结点是第二个结点有资格去尝试获得锁(因为有可能是被head 结点释放锁后唤醒),获得锁成功则把当前节点设为头结点,当前结点出队。在finally中调用cancelAcquire方法
然后判断是否应该阻塞,主要是根据前置结点的wait_status来判断,
如果前置结点状态是SIGNAL(-1),代表前置结点在park中,当前结点可以放心阻塞;如果前置结点大于1,那么代表前置结点以推出,需要不断向前循环找到未退出的结点(<=0);如果小于0 通过CAS 将当前节点状态设为SIGNAL;如果应该阻塞并阻塞后因interrupt被唤醒 将interrupted标记位设为true。


shouldParkAfterFailedAcquire

如果应该被阻塞则调用parkAndCheckInterrupt()方法来实现阻塞,该方法调用操作系统级的park方法来使线程进入waiting状态,如果线程被唤醒 则检查是不是被interrupt的,有两种方法唤醒当前线程unpark()和interrupt(),截图中的Thread.interrupted会清空当前中断标记


阻塞实现

2.tryLock的实现

trtLock直接调用nonfairTryAcquire 直接返回获取锁结果,如果加时间的会是一个死循环尝试获取锁,每次循环检查过期时间如果过期返回false;

unlock的实现

unlock实现nonfair 和fair是一致的


unlock

release

sync中的tryrelease

nonfair和fair中的tryrelease 最终调用的都是其父类Sync中的实现,将AQS中的state减1,如果state==0了,说明当前锁释放了,清空AQS独占线程,返回true;否则返回false;
上层releas 接到返回结果如果为true,证明当前锁释放,获得当前队列中最近的一个不为null的结点(下一个等待线程)使用unpark 方法将其唤醒。

  • FairLock的实现

与NonFairLock的主要不同在于,lock方法直接调用Acquire方法,不会尝试直接抢占锁


fairlock

tryAcquire 与nonFairTryAcquire的不同主要在于会通过hasQueuePredecessors方法首先检查当前线程是不是等待队列中的第一个,如果是第一个(之前没有其他的等待线程),才会尝试获得锁:


fairLock的tryAcquir实现

总结

  • NonFair 和Fair的unlock实现一致,lock方法的主要区别是nonFair会第一时间尝试抢占锁,fair会先检查等待队列,在没有前置的等待线程后才会尝试获得锁。通过AQS中的state的增加和exclusiveOwnerThread 的判断实现了可重入;通过操作系统的park方法实现了则色;通过了死循环 和 interrupt,unpark 唤醒机制 实现了自旋(spinlock)。
  • AQS(AbstractQueuedSynchronizer)该类继承顶级抽象类AOS(AbstractOwnableSynchronizer),AOS中主要维护了独占线程,来表示是哪个线程当前持有锁;AQS中比较重要的一个参数是volatile修饰的state,该参数为0 时代表当前锁是可获取的;AQS中还有一个重要的静态内部类Node,Node是等待线程队列中的具体对象,一个Node 代表一个线程,维护了其前置Node 和next node,并有status 表示当前线程所处的状态是否在沉睡,这几个状态都是由volatile修饰的;
  • AQS 是一个锁框架,它维护了等待锁线程队列,包括入队,唤醒线程,判断是否该沉睡等一些列操作,自定义的同步器(例如NonFairSync 和 FairSync)只需实现自定义的lock 和 unlock 就好~
    第一次看这块源码还有一些地方没太看懂,欢迎指正~

参考资料:

https://www.cnblogs.com/waterystone/p/4920797.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容

  • 作者: 一字马胡 转载标志 【2017-11-03】 更新日志 前言 在java中,锁是实现并发的关键组件,多个...
    一字马胡阅读 44,143评论 1 32
  • 1.解决信号量丢失和假唤醒 public class MyWaitNotify3{ MonitorObject m...
    Q罗阅读 871评论 0 1
  • 本章探讨的是关于多线程安全的获取和修改临界资源的方式。 临界资源:需要被有序访问的共享资源,指给定时刻只允许一个任...
    黑色偏幽默阅读 274评论 0 0
  • 小雨滴滴喜甚亲, 花开果硕雀晨吟。 鲜花随雨点青草, 更是难得一景新。
    耆女阅读 151评论 0 1
  • 一开始学习做思维导图的时候就都是采取手绘的方式。 我也认为手绘能更好的整理自己的思路。毕竟每一条线,每一个字都是自...
    SHEROtomorrow阅读 2,797评论 5 6