ReentrantLock是基于AQS来实现的,所以如果想对它有所了解,最好先熟悉一下AQS的内容,它继承了Lock接口,Lock接口定义了加锁释放锁的方法;
ReentrantLock支持公平锁和非公平锁两种加锁方式,而不像synchronized只支持一种非公平锁;并且ReentrantLock也支持中断非中断加锁;
1. 公平锁
ReentrantLock内部有三个内部类,Sync、NonfairSync、FairSync;其中FairSync是实现公平锁的子类,而NonfairSync和FairSync继承自Sync分别实现非公平锁和公平锁;
当new ReentrantLick()
时,默认创建的是NonfairSync,如果想创建一个公平锁,只需要new ReentrantLock(true)
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
1.1 加锁
final void lock() {
acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 如果c==0,表示当前没有人加锁,当前线程可以进行加锁处理;
if (c == 0) {
// 如果不需要排队,则修改state值,修改成功表示当前线程获得到锁;
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;
}
// 用于判断是否需要排队,返回false不用排队,否则需要排队
public final boolean hasQueuedPredecessors() {
//
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t && // head=tail=null 不需要排队,另外一种情况是只有一个节点,并且head=tail 只有一个节点也是不需要排队的,因为队列中的第一个节点是不需要排队的;
((s = h.next) == null || s.thread != Thread.currentThread());
}
1.2 解锁
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
// 释放锁
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果c!=0表示有锁重入
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
// 释放锁是在获取锁的情况下进行的,所以这里不需要CAS
setState(c);
return free;
}
2. 非公平锁
非公平锁不像公平锁那样,如果锁被占用时就会排队,并且能保证FIFO,而非公平锁在加锁时,会先进行一次锁的竞争,不管队列中是否有等待线程;
2.1 加锁
final void lock() {
// 如果进来时,正好有其他线程释放锁,那这里直接加锁,如果成功,则当前线程加锁成功; 第一次非公平
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
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;
}
对AQS有所了解了再来看ReentrantLock代码是非常简单明了的;