java并发包学习:1.ReentrantLock代码公平锁和非公平锁实现解析
2017-11-28 22:22 笔法春秋 阅读(475) 评论(0) 编辑 收藏 举报
java中ReentrantLock是一个功能比较简单的显式锁,也是一个比较好的学习java并发包的一个切入点,今天看了下ReentrantLock的代码,写个文章记录一下
先看一下ReentrantLock类的基本结构;
ReentrantLock实现了Lock接口:Lock接口定义了以下api:
void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition();
lock();的实现
在ReentrantLock中,它对lock的实现非常简单:
public void lock() { sync.lock(); }
而sync是什么呢?它是AQS(AbstractQueuedSynchronizer)的一个实现
private final Sync sync; /** * Base of synchronization control for this lock. Subclassed * into fair and nonfair versions below. Uses AQS state to * represent the number of holds on the lock. */ abstract static class Sync extends AbstractQueuedSynchronizer
AbstractQueuedSynchronizer本身是一个可以用于构建锁或者其他相关同步装置的基础框架,通过继承来实现现大部分同步需求的基础,要详细了解AbstractQueuedSynchronizer,可以看http://ifeve.com/introduce-abstractqueuedsynchronizer/ f里面有非常详细的介绍
AbstractQueuedSynchronizer提供了四个待子类重写的方法,风别是:
方法名称 | 描述 |
protected boolean tryAcquire(int arg) | 排它的获取这个状态。这个方法的实现需要查询当前状态是否允许获取,然后再进行获取(使用compareAndSetState来做)状态。 |
protected boolean tryRelease(int arg) | 释放状态。 |
protected int tryAcquireShared(int arg) | 共享的模式下获取状态。 |
protected boolean tryReleaseShared(int arg) | 共享的模式下释放状态。 |
protected boolean isHeldExclusively() | 在排它模式下,状态是否被占用。 |
同时Syn重写了AbstractQueuedSynchronizer的两个方法
protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; } protected final boolean isHeldExclusively() { // While we must in general read state before owner, // we don't need to do so to check if current thread is owner return getExclusiveOwnerThread() == Thread.currentThread(); }
而在ReentrantLock中,默认Syn是NonfairSync实例
public ReentrantLock() { sync = new NonfairSync(); }
NonfairSync本身是一个非公平锁,在本文中,就先拿NonfairSync来做分析
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
大家注意到没有,在NonfairSync的实现中,它重写了AbstractQueuedSynchronizer中的tryAcquire函数,而tryAcquire的本身是一个用于获取排它锁的函数
先来看lock方法
/** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
这个方法很好理解,如果无线程持有当前锁,那么调用acquire函数
acquire是怎么做的呢?
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
它会根据tryAcquire(arg)判断是否满足获取锁的条件
而在NonfairSync,则重写了tryAcquire的逻辑,
protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
很清晰的逻辑,为了保证可重入性,实现里通过计数器和是否当前线程来判断是否能获取锁。
而其非公平性的代码体现是在
/** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread());
这块进行体现的,加入一条线程释放锁,同时另一条线程进入,则不通过SQR的阻塞队列来实现加锁,而是直接让其通过。
以上是非公平锁的lock实现,理解了非公平性加锁,再来看公平性加锁,就很容易了
以下是公平锁的初始函数:
/** * Creates an instance of {@code ReentrantLock} with the * given fairness policy. * * @param fair {@code true} if this lock should use a fair ordering policy */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
在这里,公平性锁是通过FairSync来实现的,来看看FairSync的代码
/** * Sync object for fair locks */ static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
这里主要重点是在hasQueuedPredecessors里,且看实现
public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
它的返回逻辑分析一下:head不等于tail或者(队首的线程和当前线程不一致)
那么如果hasQueuedPredecessors返回true,那也就意味着当前线程不能获取锁;而如果是false,那么当前线程是AQS队列的列首,可以获取锁,并以此来实现公平锁。
在依赖AbstractQueuedSynchronizer的情况下实现并不复杂,大部分的底层阻塞逻辑其实是在AbstractQueuedSynchronizer里做的,下一篇文章将对AbstractQueuedSynchronizer进行分析。