对于Reentranlock的用法,对比Synchronized多了 定时守候、可中断守候、公平和非公平。本文重点就Reentranlock 中的代码实现做一个分析。而对于其与Synchronize 区别可参考下文。
可以参考深入研究 Java Synchronize 和 Lock 的区别与用法http://my.oschina.net/softwarechina/blog/170859
FairSync NonFairSync 两者都是java.util.concurrent包中同步类中如Semaphere,Reentranlock 等类中的内部类,两者继承自 AQS,而Reentranlock中的 两者代码如下所示,为独占模式。两者都实现了tryAcquire,与lock方法。
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);
}
}
Reentranlock 根据参数,确定使用哪种同步器,默认是NonFairSync。从代码分析出,NonFairSync最大的特点是,调用lock时,会先尝试去获取资源,而获取不同,则通过父类的acquire(1)方法,调用各自子类的tryAcquire方法,如下。而对于NonFairSync,在一次执行父类Sync中的方法,在一次尝试获取资源。
然而对于FairSync的lock,直接调用acquire,少了一次直接获取的过程,接着调用子类的tryAcquire方法,当有资源空闲时,并不像NonFair 直接尝试get,而是先判断当前线程是否队列头部的线程,或者是空的queue,则尝试获取。否则判断当前线程是否是占用资源的线程。如果是,则将state+1,表示该线程调了多次lock,释放时要调同等次数的unlock,才能释放资源。
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() && //当前Thread是否是head节点的next节点线程,因为公平的锁,按顺序获取资源
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;
}
}
接着上一步,两者都没有获取到资源时,将执行一样的流程,但是执行到tryAcqure方法时,会根据不同子类实现的来执行。
如下代码,首先
- addWaiter,,生成一个Exclusive模式的结点,并将当前结点加入到queque的尾部。如果原tail为null,也就是queque为null时,会cas一个new Node,而后加到该node尾部。并做为新的tail。
- 而后会 执行acquireQueued方法,根据node的 predocessor,判断是否为head,如果是则执行tryAcquire, 获取成功后,将当前结点置为head,并将thread置为null,已经获得资源。
- 当node的prddocessor不为head时,或者获取失败,接着会根据waitStatus,执行相关操作,最终state置为Node.Signal,而后被阻塞,等着被unPark或者被中断。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
而对于 Reentranlock 的其它功能如 中断守候,超时等,都是针对这些条件做了额外的操作。