J.U.C剖析与解读1(Lock的实现)

J.U.C剖析与解读1(Lock的实现) 前言

为了节省各位的时间,我简单介绍一下这篇文章。这篇文章主要分为三块:Lock的实现,AQS的由来(通过演变的方式),JUC三大工具类的使用与原理剖析。

Lock的实现:简单介绍ReentrantLock,ReentrantReadWriteLock两种JUC下经典Lock的实现,并通过手写简化版的ReentrantLock和ReentrantReadWriteLock,从而了解其实现原理。

AQS的由来:通过对两个简化版Lock的多次迭代,从而获得AQS。并且最终的Lock实现了J.U.C下Lock接口,既可以使用我们演变出来的AQS,也可以对接JUC下的AQS。这样一方面可以帮助大家理解AQS,另一方面大家可以从中了解,如何利用AQS实现自定义Lock。而这儿,对后续JUC下的三大Lock工具的理解有非常大的帮助。

JUC三大工具:经过前两个部分的学习,这个部分不要太easy。可以很容易地理解CountDownLatch,Semaphore,CyclicBarrier的内部运行及实现原理。

不过,由于这三块内容较多,所以我将它拆分为三篇子文章进行论述。

一,介绍 Lock

Lock接口位于J.U.C下locks包内,其定义了Lock应该具备的方法。

Lock 方法签名:

void lock():获取锁(不死不休,拿不到就一直等)

boolean tryLock():获取锁(浅尝辄止,拿不到就算了)

boolean tryLock(long time, TimeUnit unit) throws InterruptedException:获取锁(过时不候,在一定时间内拿不到锁,就算了)

void lockInterruptibly() throws InterruptedException:获取锁(任人摆布,xxx)

void unlock():释放锁

Condition newCondition():获得Condition对象

ReentrantLock 简介

ReentrantLock是一个可重入锁,一个悲观锁,默认是非公平锁(但是可以通过Constructor设置为公平锁)。

Lock应用

ReentrantLock通过构造方法获得lock对象。利用lock.lock()方法对当前线程进行加锁操作,利用lock.unlock()方法对当前线程进行释放锁操作。

Condition应用

通过

ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition();

获得Condition对象(Condition是J.U.C下locks包下的接口)。

通过Condition对象的.await(*),可以将当前线程的线程状态切换到Waiting状态(如果是有参,则是Time Waiting状态)。而.signal(),.signalAll()等方法则正好相反,恢复线程状态为Runnable状态。

ReentrantReadWriteLock 简介

ReentrantLock和Synchronized功能类似,更加灵活,当然,也更加手动了。

大家都知道,只有涉及资源的竞争时,采用同步的必要。写操作自然属于资源的竞争,但是读操作并不属于资源的竞争行为。简单说,就是写操作最多只能一个线程(因为写操作涉及数据改变,多个线程同时写,会产生资源同步问题),而读操作可以有多个(因为不涉及数据改变)。

所以在读多写少的场景下,ReentrantLock就比较浪费资源了。这就需要一种能够区分读写操作的锁,那就是ReentrantReadWriteLock。通过ReentrantReadWriteLock,可以获得读锁与写锁。当写锁存在时,有且只能有一个线程持有锁。当写锁不存在时,可以有多个线程持有读锁(写锁,必须等待读锁释放完,才可以持有锁)。

Lock及Condition应用 ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); readLock.lock(); readLock.unlock(); readLock.newCondition(); ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); writeLock.lock(); writeLock.unlock(); writeLock.newCondition();

与之前ReentrantLock应用的区别,就是需要通过lock.readLock()与lock.writeLock()来获取读锁,写锁,再进行加锁,释放锁的操作,以及Condition的获取操作。

二,手写ReentrantLock 获取需求

终于上大餐了。

首先第一步操作,我们需要确定我们要做什么。

我们要做一个锁,这里姑且命名为JarryReentrantLock。

这个锁,需要具备以下特性:可重入锁,悲观锁。

另外,为了更加规范,以后更好地融入到AQS中,该锁需要实现Lock接口。

而Lock的方法签名,在文章一开始,就已经写了,这里不再赘述。

当然,我们这里只是一个demo,所以就不实现Condition了。另外tryLock(long,TimeUnit)也不再实现,因为实现了整体后,这个实现其实并没有想象中那么困难。

JarryReentrantLock实现原理

既然需要已经确定,并且API也确定了。

那么第二步操作,就是简单思考一下,如何实现。

类成员方面:

首先,我们需要一个owner属性,来保存持有锁的线程对象。

其次,由于是可重入锁,所以我们需要一个count来保存重入次数。

最后,我们需要一个waiters属性,来保存那些竞争锁失败后,还在等待(不死不休型)的线程对象。

类方法方面:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyxdzg.html