ReentrantLock
基本介绍引言
Lock在JavaSe5之后出现,相比于Synchronized拥有锁获取和释放的可操作性、可中断等同步特性。Synchronized是基于进入和退出monitor对象来实现方法和代码的同步的(monitorenter和monitorexit),那Lock又是基于什么是实现的呢?没错就是大家经常听到的AQS(同步队列器). 首先我们可以看下current包下面的ReentrantLock的一个类图
ReentrantLock的整个类结构比较简单,实现了Lock接口,内部还组合了内部类Sync以及Sync两个子类。NonfairSync和FairSync.Lock接口的实现是ReentrantLock的对外调用能力的封装,而具体的实现则是依赖内部类Sync的父类AbstractQueuedSynchronizer提供的封装。我们先来看看Sync的一个类结构图
ReentrantLock对Lock接口的实现实际上则又完全是对内部类Sync的方法的一个调用譬如
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
FairSync和NonFairSync
在讲AQS之前我们先简单了解一下ReentrantLock的公平锁和非公平锁的区别,ReentrantLock提供了两个构造方法
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
FairSync和NonFairSync的代码如下
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
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;
}
}
可以看出不管是lock还是tryAcquire方法,非公平锁相对于公平锁实际上都只多了一个步骤,就是一上来就去申请锁直接CAS,而公平锁是直接进入FIFO队列有序获取,非公平锁当一进入申请锁失败之后就和公平锁一样了,这个里面没有unlock也说明了unlock两个锁之间并无区别。
Sync
接下来我们看看Lock接口的是怎么被Sync实现的这里我们就先挑几个核心的方法
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
//这个lock方法由前面两个子类实现
abstract void lock();
//被AQS的模板方法release调用
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;
}
//非公平锁的tryAcquire直接调用此方法
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;
}
final ConditionObject newCondition() {
return new ConditionObject();
}
}
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer
implements java.io.Serializable {
//ReentrantLock的unlock直接调用这个方法
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
}
可以看出Sync的方法实现基本就是对父类AQS方法的一个按需调用,我们自己写一个锁也是一样只要去按需实现AQS里面的一些protected方法比如上面的tryAcquire。
AQS
到这我们可以看看AQS为我们锁的实现提供了一些什么东西,如果我们不依赖AQS的话你会想到需要什么?
1.一个state变量,记录锁的状态,那么它至少要有0,1两个状态。同时对state操作要保证线程安全,也就是需要CAS
2.需要记录持有当前锁的线程
3.需要底层支持对线程的阻塞和唤醒操作
4.需要有一个队列维护所有的阻塞线程,该队列也必须线程安全也需要支持CAS
那我们再看看AQS里面对应的一些属性和方法吧。
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
static final class Node {
........暂时不表.......
}
//队列头节点
private transient volatile Node head;
//队列尾结点
private transient volatile Node tail;
//state的取值不仅可以是0,1还可以大于1,这样就可以支持可重入锁
private volatile int state;
}
//state的CAS
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
//头节点CAS
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
//尾节点CAS
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
}
/**
*AQS的的阻塞唤醒则用了LockSupport封装的Unsafe类中的park/unpark,
*当调用park线程阻塞,另一个线程调用unpark(Thread t),被传入的阻塞线程就可以被精准唤醒
*/
public class LockSupport {
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
}
具体的AQS实现细节在此先不细讲,后面有时间再开一篇文章,接下来我们再开看看Lock里面的Condition是个什么?
Condition
开头我们说过Synchronized是依靠进出对应的monitor对象来实现的,同时wait(),notify等方法存在可以实现等待/通知模式,Condition接口也提供了类似Object上的这几个方法,与Lock配合可以实现等待/通知模式。Condition必须通过Lock的newCondition()方法获取。
public interface Condition {
void await() throws InterruptedException;
void awaitUninterruptibly();
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal();
void signalAll();
}
AQS内部类ConditionObject实现了Condition接口,并且维护了一个等待队列,节点类型复用了AQS中的节点定义
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;
/**
*使当前线程进入等待队列并释放锁,状态变为等待状态。将AQS中的首
*节点加入到Condition的等待队列
*/
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//当前线程加入等待队列
Node node = addConditionWaiter();
//释放锁
int savedState = fullyRelease(node);
int interruptMode = 0;
//判断是否存在于AQS队列中,只有被unpark唤醒的才会加入,中断的不会
while (!isOnSyncQueue(node)) {
//自己阻塞自己
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//重新拿锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
//唤醒同步队列的第一个线程
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
//先将node放入AQS同步队列中,再调用unpark
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
..................其他方法实现暂时省 ...............
}
conditionObject其他方法也类似,接下来我们可以看下condition的实际应用ArrayBlockingQueue
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
/** The queued items */
final Object[] items;
final ReentrantLock lock;
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();//put时候队列满了则阻塞于非满条件
enqueue(e);
} finally {
lock.unlock();
}
}
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();//put成功通知非空条件
}
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();//队列为空的时候,take阻塞于非空条件
return dequeue();
} finally {
lock.unlock();
}
}
}
可以看出通过Condition我们可以很好的实现阻塞队列。