首先参考http://www.cnblogs.com/skywang12345/p/3496098.html(多线程系列讲得挺好的)
什么是ReentrantLock?
ReentrantLock是独占锁、可重入锁。所谓独占锁,是指只能被独自占领,即同一个时间点只能被一个线程锁获取到的锁。所谓可重入锁,ReentrantLock锁可以被单个线程多次获取。
(01) ReentrantLock实现了Lock接口。
(02) ReentrantLock中有一个成员变量sync,sync是Sync类型;Sync是一个抽象类,而且它继承于AQS。
(03) ReentrantLock中有”公平锁类”FairSync和”非公平锁类”NonfairSync,它们都是Sync的子类。ReentrantReadWriteLock中sync对象,是FairSync与NonfairSync中的一种,这也意味着ReentrantLock是”公平锁”或”非公平锁”中的一种,ReentrantLock默认是非公平锁。
源码分析
public class ReentrantLock implements Lock, java.io.Serializable {
...
}
首先从整体来看,ReentrantLock是一个实现Lock与java.io.Serializable接口的类。
父接口:Lock类
public interface Lock {
//获取锁的方法
void lock();
//如果可以获取锁,则立即返回;获取锁的时候优先相应中断(而不是获取锁),并且抛出中断异常
void lockInterruptibly() throws InterruptedException;
//如果可以获得锁,则返回true;如果锁被其他线程获取,则返回false;
boolean tryLock();
//与tryLock相比,设置等待时间,如果在设置的时间之内获得了锁,则返回true;不能获得锁,或者没有在设置的时间内获得锁,则返回false;并且当等待获得锁的时候被中断,则抛出中断异常。
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//释放锁;
void unlock();
//返回一个新的Condition实例;
Condition newCondition();
}
父接口:java.io.Serializable
java.io.Serializable是一个标记接口,没有任何的抽象方法,其作用是表明实现此接口的类可以进行序列化和反序列化,Java的”对象序列化”能让你将一个实现了Serializable接口的对象转换成byte流,这样日后要用这个对象时候,你就能把这些byte数据恢复出来,并据此重新构建那个对象了。
接下来继续看源码,发现ReentrantLock中有好几个内部类;
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
abstract void lock();
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;
}
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();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
Sync的官方API解释:基于锁的同步控制,其子类分为公平和非公平两个版本,使用AQS(AbstractQueuedSynchronizer)的状态代表对锁的持有数量。
Sync类继承AbstractQueuedSynchronizer类。在Sync的nonfairTryAcquire(int acquires)方法中出现了一个int型变量c。其值为getState(),getState()方法是AQS中的一个方法,其返回一个状态,此状态在Sync中作为一个线程持有的锁数量;当持有锁的数量为0时,它会设置占有锁的线程为当前线程,并且返回true。如果持有锁的数量不为0,则首先判断当前线程是否是一个独占锁的线程,然后设置线程持有锁的数量为初始数量c加上acquires数量的值(表现出可重入的特性)。
接下来就是Sync的两个子类,NonfairSync与FairSync
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);
}
}
lock()方法表示创建一个非公平的独占锁,并且将锁的状态即线程持有锁的数量设置为1。若当前线程持有锁,则执行tryAcquire()方法。
tryAcquire()方法直接调用的是Sync的nonfairTryAcquire()非公平获取锁的方法。
公平锁源码如下:
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;
}
}
FairSync公平锁,在创建锁时要执行公平策略:如果锁被占用且当前线程不是持有者也不是等待队列的第一个,则进入等待队列(AQS就是一个先进先出的等待锁队列)。
公平锁创建锁时,直接依赖执行tryAcquire()方法。
公平版本的tryAcquire(),当锁空闲时即c=0,会判断有没有等待队列和当前线程是不是等待队列的第一个,若没有等待队列或者是等待队列的第一个,且会尝试去利用CAS获取锁,成功之后就会设置锁的持有者为当前线程,这就是在获取锁的公平获取策略。同样若锁被当前线程持有,则增加线程持有锁的数量,即可重入。
非公平锁和公平锁都是继承Sync,所以形式上相同,唯一的区别是当当前线程不持有锁时,非公平锁将AQS状态设置为1,然后设置当前线程为锁的持有者。为公平锁则是先尝试去获取锁,若能够获取锁,再将当前线程锁持有状态(AQS)设置为1。
了解完ReentrantLock锁里面的内部类之后,其类中的其他方法都是依赖内部类来实现的,所以很好理解了。
//无参构造方法,默认是非公平的锁。
public ReentrantLock() {
sync = new NonfairSync();
}
//通过设置是否公平,来构造ReentrantLock锁;当为true时,构造公平锁,为false时,构造非公平锁。
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
//获取锁,调用的是对应非公平锁和公平锁中的lock()方法
public void lock() {
sync.lock();
}
//响应锁的中断,能够在获取锁时优先响应中断并抛出中断异常
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
//尝试获取锁,调用的是非公平获取锁的方法
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
//设置获取锁的时间限制
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
//释放锁
public void unlock() {
sync.release(1);
}
//Condition的作用是对锁进行更精确的控制,之后学习。
public Condition newCondition() {
return sync.newCondition();
}
//获得当前线程锁的持有数量。
public int getHoldCount() {
return sync.getHoldCount();
}
//判断当前是不是持有锁
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
//判断锁的状态,若任何线程持有锁则返回true,否则返回false
public boolean isLocked() {
return sync.isLocked();
}
//判断锁是不是公平锁。
public final boolean isFair() {
return sync instanceof FairSync;
}
//用来返回持有此锁的当前线程,若锁不被任何线程持有,则返回空。
protected Thread getOwner() {
return sync.getOwner();
}
//判断这个锁是否有一个等待获取此锁的队列
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
//判断给定的线程是否是在等待获取锁的队列中,若是,返回true
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
//返回锁等待队列的长度
public final int getQueueLength() {
return sync.getQueueLength();
}
//获取锁等待队列中的线程集合
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
//下面的几个方法与Condition有关,之后再详细学习。
上面的方法都是有关获取锁、释放锁以及等待锁队列,ReentrantLock实际上与AQS有很大的关系,因为其实现的方法中都与Sync有关系,而Sync继承AQS,所以方法中有许多关于锁等待队列的判断及获取方法。ReentrantLock利用内部类来隐式的继承AQS,用户不会看到不需要的方法,也避免了用户错误地使用AbstractQueuedSynchronizer的公开方法而导致错误。
ReentrantLock锁源码大概就这么多,总结一下:ReentrantLock锁里有一个很重要的内部类Sync,其继承AQS类,有了锁的等待队列特性,同时Sync衍生出来两种锁:非公平锁和公平锁,利用等待队列的特性可实现公平获取锁的策略,用户可依据ReentrantLock去创建公平锁与非公平锁。在ReentrantLock方法中继承了Lock类的可中断锁的特性,以及获取锁等待队列的相关信息。