文章目录
1 概要
ReentrantLock 通过实现Lock接口的行为,提供锁机制。但是实现委托给了内部的Sync,Sync extends AbstractQueuedSynchronizer,继承了AQS的能力。此时还提供两个具体的实现,公平锁和非公平锁。首先如果对AQS不了解,请看java并发编程 AbstractQueuedSynchronizer(AQS)详解一。下文会对上述几个点进行详解内部原理
2 相关文章
3 例子
ReentrantLock 注释上的例子。。。。
如果lock没有被阻塞住就代表获取到锁,然后执行业务逻辑。最终finally 里释放锁,防止抛异常
java
public class X {
private final ReentrantLock lock = new ReentrantLock(); // ...
public void m() {
lock.lock();
// block until condition holds
try {
// ... method body
} finally {
lock.unlock() ;
}
}
}
4 方法详解
先看非公平锁实现。
先说下在ReentrantLock里上锁是通过state变量,如果是0,且从0原子变成1成功代表获取成功,如果重入则state + 1,释放锁就减1,0的时候释放锁。
4.1 lock()
java
public void lock() {
//委托给sync执行
sync.lock();
}
//非公平锁实现
final void lock() {
//先自己尝试设置成1 如果成功设置拥有锁的线程为自己
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//aqs 的acquire 若对aqs不熟悉的,请先看相关文章
//他会进入tryAcquire(arg)的具体实现
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) {
//如果是0 尝试变成1,此时如果阻塞队列中有阻塞的线程,但是新的加锁线程还是有可能获取到锁的,
//因为释放锁后只会从Head.next的Node去唤醒获取锁, 你后来的线程比先来的先拿到锁,公平吗? 非公平锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//可重入的实现。如果当前线程是自己,也就是lock拿到锁再lock直接state + 1, 因为独占锁,所以不需要原子性+1
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//现在state不是0且持有锁的线程不是自己,尝试加锁失败
return false;
}
4.2 unlock()
持有锁的线程释放锁
java
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
// aqs的抽象实现
if (tryRelease(arg)) {
//成功了会唤醒head.next线程
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
//释放失败 可重入的时候从5 -> 4
return false;
}
protected final boolean tryRelease(int releases) {
//不需要原子性操作是因为当前持有锁
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//state = 0 的时候代表释放锁
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
4.3 tryLock()
对比lock 其实就没有进入阻塞队列的逻辑。比较简单
java
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
4.4 其他
其他方法都可类比lock 和 unlock。如阻塞一段时间的等。
公平锁
公平锁核心方法实现,对比下和非公平锁的区别就可以看到,多了!hasQueuedPredecessors()
这个方法。很清晰。
java
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//区别在这,如果阻塞队列有阻塞的线程,就不去争抢,会return false
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;
}
//阻塞队列中没有阻塞的线程
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
总结
ReentrantLock 本质上是基于AQS实现的可重入锁,且提供了公平和非公平的机制,逻辑较为简单,需要对AQS熟练掌握。