Lock、ReentrantLock、ReentrantReadWriteLock
Lock相比于synchronized具有更强大的功能,在jdk1.6之前,锁竞争激烈的情况下使用lock的实现类ReentrantLock甚至比synchronized具有更好的性能,1.6之后oracle对synchronized进行了优化,如今的jdk1.8中两者性能不相伯仲。一个工具类,没有使用机器指令,没有编译器的特殊优化,却具有和jvm层实现的关键字一样甚至更好的效率与更强大的功能。对Doug Lea大神再一次膜拜Orz 本文讲详细介绍高效锁结构的实现原理与使用方式。
Lock
Lock是一个接口,定义了锁的基本方法:
public interface Lock {
void lock();//加锁
void lockInterruptibly() throws InterruptedException;//加可中断锁
boolean tryLock();//加锁成功返回true,失败返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//加锁成功返回true,失败等待一段时间后若仍无法加锁则返回false,可响应中断
void unlock();//解锁
Condition newCondition(); //返回一个Condition,利用它可以如和Synchronizd配合使用的wait()和notify()一样对线程阻塞和唤醒,不同的是一个lock可以有多个condition.
}
最后的newCondition返回一个Condition对象,该对象是一个接口,我们来看看其中提供的方法。
public interface Condition {
void await() throws InterruptedException;//类似于wait(),可以响应中断
void awaitUninterruptibly();//不响应中断的等待
long awaitNanos(long nanosTimeout) throws InterruptedException;//等待指定时间(单位是纳秒),在接到信号、被中断或到达指定等待时间之前一直处于等待状态。方法返回被唤醒后的剩余等待时间,若返回值小于等于0则代表此次等待超时。
boolean await(long time, TimeUnit unit) throws InterruptedException;//指定时间到达前结束等待返回true,否则返回false
boolean awaitUntil(Date deadline) throws InterruptedException;//指定日期到达前被唤醒返回true,否则返回false
void signal();//唤醒一个等待中的线程,类似于notify()
void signalAll();//唤醒所有等待中的线程,类似于notifyAll()
}
ReentrantLock
可重入锁是Lock接口的一个重要实现类。所谓可重入锁即线程在执行某个方法时已经持有了这个锁,那么线程在执行另一个方法时也持有该锁。首先我们来看看加锁方法lock的实现
public void lock() {
sync.lock();
}
sync是ReentrantLock中静态内部接口Sync的实例对象。在ReentrantLock中提供了两种Sync的具体实现,FairSync与NonfairSync。故名思意,两种不同的Sync分别用于公平锁和非公平锁。首先我们来看FairSync中lock的实现
final void lock() {
acquire(1);
}
acquire方法在Sync的父类AbstractQueuedSynchronizer中实现
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
感觉代码大致含义是:尝试获取锁,成功则直接返回(&&链接,前一个为fales不会再进入后一个),失败则尝试加入等待队列中,若还是失败则触发中断。下面我们做具体分析。
首先分析tryAcquire()方法
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
在这里只是一个空实现,具体的实现又交回了子类FairSync中,这里用到了模板方法设计模式,关于这个设计模式的相关知识可以戳这里http://blog.csdn.net/lovelion/article/details/8299794
那么为什么这个空方法Doug Lea不以抽象的方式声明呢?因为AbstractQueuedSynchronizer有两种功能:独占和共享,之后我们在分析ReentrantReadWriteLock将详细看到。若都定义为抽象方法会导致子类无论如何都需要实现另外一种场景的抽象方法,显然,这对子类来说是不友好的。到底是哪两种使用场景稍后我们可以具体看到。
继续来看看FairSync中的tryAcquire()方法
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;
}
getState()是AbstractQueuedSynchronizer中的方法,其有一个标志位state
private volatile int state;
protected final int getState() {
return state;
}
这个标志位的作用就是表示有没有线程已经拿走了锁,即当前锁还是否存在。
state为0表示锁没被取走,接着进入下一层判断
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
hasQueuedPredecessors判断当前线程之前是否还有其他线程在等待锁。如果当前线程是等待队列中的头节点或线程等待队列为空则返回fales(表示当前线程之前无其他节点)
public final boolean hasQueuedPredecessors