ReentrantLock解析

ReentrantLock解析

ReentrantLock是Java.util.concurrent包中提供的锁。是Lock接口的默认实现,排他锁(独享锁),相对于Synchronized而言,ReentrantLock提供了更细粒度的加锁方式以及更多的操作方式。

ReentrantLock特点

  • 可重入;
  • 可中断
  • 分为公平锁和非公平锁,默认为非公平锁

它的使用方式为

1
2
3
4
5
6
ReentrantLock lock = new ReentrantLock();
lock.lock();

// do busniess

lock.unlock();

可以在new的时候指定使用公平锁还是非公平锁。

默认的构造方法,ReentrantLock()创建的是非公平锁。调用ReentrantLock(bool fair)这个构造方法,传入true,既可以创建一个公平的ReentrantLock锁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}

/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

ReentrantLock类结构

参见下图,简单梳理了以下,ReentrantLock的类结构图

ReentrantLock类结构

  • ReentrantLock实现了Lock,Serializable接口
  • 内部使用了内部类Sync来实现主要的加锁、解锁逻辑
  • 通过继承了Sync类的NonFairSync和FairSync实现了非公平锁和公平锁的逻辑
  • Sync内部类继承了AQS

AQS相关内容可参见Java AQS 解析

ReentrantLock加/解锁流程分析

加锁

1
2
3
public void lock() {
sync.lock();
}

可以看到,代码就是简简单单一句sync.lock().而这块的sync,根据创建的不同选择,可以是公平锁和非公平锁。那么接下来再看看公平锁和非公平锁是怎么处理这块逻辑的。

公平锁

1
2
3
4
5
6
7
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;

final void lock() {
acquire(1);
}
}

它也是简简单单一句acquire(1).追踪进去,发现它是调用的AbstractQueuedSynchronizer类中的acquire方法。具体逻辑请参考文章Java AQS 解析

我们看一看公平锁自己实现的tryAcquire();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 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;
}

它首先获取state的值,如果,state为0,表示当前锁时可用的,然后看看等待队列里面是否还有线程在等着,有就不能尝试获取锁,必须排队;

如果state不为0,则判断当前线程是不是就是已经占用锁的那个独占线程,是的话,修改state,期间还判断下state是否溢出。

非公平锁

非公平锁流程上和公平锁类似,区别就是:

  • lock的时候先直接lock,失败的话,在尝试重新获取

  • 在tryAcquire的时候,没有判断队列中是否有线程在等待这一步,也就是没有这个过程(!hasQueuedPredecessors() ).

看下它的lock方法

1
2
3
4
5
6
7
8
9
10
/**
* 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);
}

NonFairSync非公平锁的acquire同FairSync一样,还是调用的父类AQS的acquire,这里不做细解。

它在lock的时候,直接通过CAS操作取设置state为1,如果成功了,则抢占锁成功,直接把独占线程设置为自己,否则,再通过AQS的acquire获取锁。

接下来看下NonFairSync的tryAcquire方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}

/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
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;
}

从上面的代码可以比较出,它和FairSync同样功能的方法是极其类似的,就是少了一句(!hasQueuedPredecessors() )。

解锁

ReentrantLock的解锁方法,unlock也是简单的调用了一下Sync的release方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Attempts to release this lock.
*
* <p>If the current thread is the holder of this lock then the hold
* count is decremented. If the hold count is now zero then the lock
* is released. If the current thread is not the holder of this
* lock then {@link IllegalMonitorStateException} is thrown.
*
* @throws IllegalMonitorStateException if the current thread does not
* hold this lock
*/
public void unlock() {
sync.release(1);
}

解锁在这块就没有公平和非公平之分了,统一调用的是AQS中的release方法。请参考文章Java AQS 解析

而AQS的release方法中则调用了Sync中实现的tryRelease方法。

1
2
3
4
5
6
7
8
9
10
11
12
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;
}

主要流程是:

  1. 改变状态值
  2. 如果当前线程不是占有锁的独占线程,抛出异常。只能由占有锁的线程释放锁
  3. 判断状态值是否已经为0了,是的话,说明锁已经没有线程占用了,自由了
  4. 设置状态值,返回
0%