ReentrantLock
不同于 synchronized
,他是一个普通的类
synchronized
通过机器指令 monitorenter
和 monitorexit
实现同步
ReentrantLock
基于 AQS
(AbstractQueuedSynchronizer
) 来实现,并且一个线程获取了锁之后可以反复的加锁,不会出现阻塞自己的情况。
一、锁类型、分为公平锁和非公平锁
/**
* 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();
}
通过构造方法构建,默认为 NonfairSync
非公平锁,非公平锁的效率要远好于公平锁。
二、公平锁获取锁
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
/**
* Sync object for fair locks
*/
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;
}
}
调用acquire(1)
尝试获取锁
1、首先判断当前状态是不是无锁状态,通过 getState()
是否等于0:
/**
* Returns the current value of synchronization state.
* This operation has memory semantics of a {@code volatile} read.
* @return current state value
*/
protected final int getState() {
return state;
}
state为reentranLock中存储的锁的状态。
2、然后会通过 hasQueuedPredecessor()
判断是否有其他正在等待的获取锁的线程:
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
如果有的话则不会尝试获取锁(这是公平锁具有的情况)
如果不存在则会调用 compareAndSetState()
将state
的值修改为1,也就是获取到锁,获取到锁之后会调用setExclusiveOwnerThread
来设置当前的独占线程
3、如果 state
的值不为0,则需要判断当前线程是否能够重入锁,如果是的话需要将当前state + 1
并更新。
4、如果 tryAquire()
失败则需要将当前线程添加到等待队列中。写入之前需要将当前线程包装一下addWaiter(Node.EXCLUESIVE)
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
首先判断队列是否为空,如果不为空则将包装好的Node
写到队列的队尾,如果发现写入失败则会调用enq()
来写入:
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
这里相当于利用自旋
+ CAS
保证一定能写入队列
5、写入队列之后需要将当前线程挂起 acquireQueued()
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
首先会根据 node.predecessor()
获取上一个被挂起的线程,如果上一个线程为头结点便会尝试获取一次锁,获取成功便结束
获取失败之后会调用 shouldParkAfterFailedAcquire(p, node)
判断当前线程是否需要挂起,根据 withStatus
withStatus存储了挂起线程的状态,如节点等待和节点取消等,如果需要挂起则会调用 parkAndCheckInterrupt()
:
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
这里使用LockSupport.park(this)
来挂起线程,直到被唤醒。
三、非公平锁获取锁
非公平锁获取锁获取锁的时候是抢占模式,每一个线程都不会去管队列如何,直接尝试去获取锁:
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);
}
}
同时自然在tryAcquire
的过程中也不需要判断队列的状态以及更新队列
四、释放锁
公平锁和非公平锁释放锁的过程都是一样的:
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;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
首先会判断当前线程是否为获得锁的线程,由于是重入锁所以需要将 state
减到 0 才认为完全释放锁。
释放之后调用 unparkSuccessor(h)
来唤醒被挂起的线程。
总结:
由于公平锁需要关心队列的情况,得按照队列里的先后顺序来获取锁(会造成大量的线程上下文切换),而非公平锁则没有这个限制。
所以也就能解释非公平锁的效率会被公平锁更高。
网友评论