线程安全往往需要靠互斥锁机制来保证,ReentrantLock是比关键字sychronized更灵活的可重入互斥锁。ReentrantLock在不使用锁(它自己本身就是Lock)和synchronized关键字的前提下,是怎么保证线程安全的呢。
看一下不公平锁实现的lock方法(调用的是Sync(NonfairSync)的lock方法
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);
}
1. 先通过CAS原子操作来试图获取锁(当前状态==0并且成功的将状态改为1),如果CAS返回true,说明成功获取到锁,那么简单的将该锁的拥有线程设为当前线程即可返回。锁获取成功。
2. 如果CAS返回false,则调用acquire方法,其中先调用tryAcquire方法,在不公平锁的实现中,tryAcuqire方法首先做的操作与lock方法的第一步操作基本重复,也是先通过CAS原子操作尝试获取锁,成功则设置所谓当前线程所有。如果当前的状态不为0(说明有线程已经获取到该锁),则判断持有该锁的是不是当前线程自身,如果是自身,由于是可重入锁,则简单的将当前状态计数+1就可返回,锁获取成功。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//tryAcquire
/**
* 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;
}
3. 如果不是上面这2种情况,则说明锁已经被其他线程排他占用,则调用addWaiter先将当前线程放入等待队列中,然后在acquireQueued方法中,循环判断是否已经轮到当前节点(即该节点前面没有可用节点),如果没有有效节点,则再尝试调用tryAcquire方法,失败则继续循环,否则成功获取到锁并返回。如果前面有其他节点,则将当前线程park,当前线程阻塞,可能直到其他线程unlock调用unpark,然后才恢复执行并重新试图获取该锁。
//this method is implemented in AbstractQueuedSynchronizer
/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
*
* @param node the node
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*/
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);
}
}