Lock包相关

本文详细解读了Java并发包中Lock接口与类的实现细节,特别是ReentrantLock与ReentrantReadWriteLock类。通过剖析AQS(AbstractQueuedSynchronizer)作为核心基础类的作用,以及其内部的Node与ConditionObject类,揭示了独占锁与读写锁的工作原理。文章还对比了ReentrantLock与ReentrantReadWriteLock之间的差异,并提供了源码理解的思路。

Lock包相关

java.util.concurrent包的子包。主要有ReentrantLockReentrantReadWriteLock两个类。看了好几篇还是没有看透,先记一下吧,以后再看。

接口与类的总览

ReentrantLock实现Lock接口,为独占锁。RenentrantReadWriteLock实现了ReadWriteLock接口,为读写锁。但是基础类却是AbstractQueuedSynchronizer抽象类(简称AQS),以及他的子孙类。但是主要代码还是在AQS中。此外对应Object等待通知模型的Condition接口实现类ConditionObject也在AQS中。
ReentrantLockRenentrantReadWriteLock中有几个内部类,但是和前两个类一样,都只是AQS的皮包公司。

实现基石类AQS

说到AQS就不得不先说他的两个内部类,分别是NodeConditionObject类。

AQS内部类
  1. Node 节点类,因为AQS内部和ConditionObject都有一个基于链表的队列数据结构。两者有关联但是又不同,这在Node可以看出。
    Node主要封装的是线程字段thread与状态字段waitStatus
    prevnext是一对属性,用于AQS的等待队列,构建了一个双向链表的队列。
    nextWaiter 用于ConditionObject,只能构建一个单向链表的队列。 SHAREDEXCLUSIVE用于读写锁,表明这个节点是独占的,还是共享的。

  2. ConditionObject 实现Condition接口,对应了Object的等待/通知模型,同一个锁,可以有多个ConditionObject对象,进而可能达到分组通知目的。
    firstWaiter/lastWaiter,这是队列的头与尾,结合Node中的nextWaiter就构成了一个单向队列。
    await()/awaitNanos()/awaitUntil()对应的是wait()系列方法。而且awaitUntil()规定确定时间点,更人性化。
    signal()/signalAll()对应的是notify()/notifyAll()方法。
    当一个线程调用await()系列方法时,会调用LockSupport中的park()方法阻塞自己,同时将线程封装加入到本Condtion的等待队列中。当有其他线程调用signal()系列方法时,会唤醒firstWaiter,将其Node加入Condition所属的AQS的执行队列中。这个逻辑与Object的通知模型类似。

AQS的原理

AQS中存在大量的CAS操作(Compare And Set),阻塞与唤醒线程调用LockSupport类,但是两者最后调用的都是sum.msic.Unsafe中方法。从名字可以看出,这个类是用于执行低级别、不安全操作的方法集合,这也是Lock相关类的基石。
AQS内部维护一个双向链表结构的队列,按FIFO的规则按顺序执行相关线程。AQS中有不少复杂的代码,我没有转出来,有兴趣的可以去看看。

ReentrantLock类的皮

可重入独占锁,是AQS的一层皮,划模式的话,我认为是门面模式了。有三个内部类。

ReentrantLock的内部类
  1. Sync
    继承自AQS,对公平锁相关做了一点处理。

  2. FairSync/NonfairSync公平/非公平
    都继承自Sync,可以在lock()方法上看到差异。NonfairSync会尝试去抢占锁。而FairSync不会。

ReentrantLock内部
  1. lock()/unlock()
    synchorized同步作用块对应。必须成对出现。

  2. tryLock()
    只有在没有其他线程独占的情况之下,才会获取锁。

  3. sync
    根据传入参数会实例化FairSyncNonfairSync,默认是后者。

ReentrantReadWriteLock类的皮

可重入读写锁,也是AQS的一层皮,有一个内部类。感觉上AQS更像是为ReentrantReadWriteLock设计。
ReentrantReadWriteLock实现的是ReadWriteLock接口,而不是Lock接口,但是他在两个内部类是实现了后者。

ReentrantReadWriteLock内部类
  1. Sync
    继承AQS,与之前ReentrantLock类中的Sync作用一样,不过对读写这个特性做了处理。尤其是对int字段的位操作,我一看就晕了 。

  2. FairSync/NonfairSync 公平/非公平同步器
    在内部对读与写操作,做了不同的阻塞判断。可以看到非公平锁的读操作会判断执行队列第一个节点是不是独占节点,如果是读操作就必须阻塞,反之如果不是,就不用阻塞。

  3. ReadLock/WriteLock 读写锁
    实现自Lock接口,也是对sync的一层包装,ReentrantReadWriteLock实例化之前都会实例化一个ReadLockWriteLock对象。

ReentrantReadWriteLock内部
  1. 读写规则
    读写锁的规则是读读之间是可以共享,写写/读写/写读不能。而同一个读写锁操作的都是同一个sync。所以能够如此配合,在AQS内部,可以看到共享与独占请求处理逻辑的不同。共享请求锁doAcquireShared方法主体中是没有park()阻塞操作的。

  2. ReentrantLock对比
    两者实现的接口不同,ReentrantLock更像是一个阉割版本的ReentrantReadWriteLock,所以先从ReentrantReadWriteLock入手,到AQS看源码,逻辑反而更清晰。

最后

水平有限,还是没有搞清AQS,虽然我更喜欢佛家说的顿悟,但是实现中渐悟靠谱一点。分量分层次来学习Lock包吧,先记一下怕忘了。

转载于:https://my.oschina.net/markho/blog/669416

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值