可重入:一个线程可以连续多次获得锁
加锁
Lock.lock() 获取锁
final void lock() {
acquire(1);
}
很简单,就是调用acqire函数,后面的参数1表示,如果获得了锁,对于另外进程释放而获取获取的,那么就锁的值就是1,否则如果是重入的值+1
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
首先通过tryAcquire获取锁,如果没有获取,那么首先通过addWaiter把当前线程放到等待队列的最后面,并阻塞它。
而在tryAcquire中,根据是否公平锁有两个版本,先看公平锁
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;
}
这个函数就是尝试去获取锁,返回是否获取了锁。首先获取锁的状态,看是否为0,如果是0,那么当前是没有线程占据锁的,那么判断当前线程是否在队列的第一个,如果是那么通过原子的compareAndSetState获取锁,把当前线程设置为锁的获得者,返回true。如果当前锁是被线程占领的,那么看下是不是被当前线程占了,如果是的话,把对应的持有锁的次数+acquires。 如果没有成功获取锁,那么就会调用addWaiter和acquireQueued两个函数,他们两个分别是把线程放到了锁的队列最后面,然后把线程给阻塞了。
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;
}
以当前的线程为信息,新建一个节点放到列表的最后面。
然后来看下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);
}
}
这块代码不断循环做两件事,第一件判断当前线程是不是队列的最前面,如果是的话,那么再次尝试获取锁,成功就把当前节点设置为头并返回是否被中断过。 否则就干第二件事,调用shouldParkAfterFailedAcquire决定是否把当前进行给阻塞,如果需要那么通过parkAndCheckInterrupt把线程阻塞,阻塞成功把interrupted设置true。
我们在来看下shouldParkAfterFailedAcquire这个函数
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
首先看当前线程的前个节点的状态,如果是SIGNAL,那么他们都没有获取锁,那就直接返回true表示进入阻塞状态
其次:把前面节点中是cancelled状态的给删除了,把当前节点作为第一个非cancelled进程的下游
否则就设置前继的状态为SIGNAL,这样下次循环可以进入1而进入阻塞
如果需要阻塞,通过下面函数完成
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
阻塞,如果阻塞结束后,返回线程的中断状态。
# 参考
http://blog.csdn.net/yanyan19880509/article/details/52345422