首先是测试代码
private final static int THREAD_NUM=5;
private final static CountDownLatch count =new CountDownLatch(THREAD_NUM);
private static Lock lock=new ReentrantLock();
private static class TestLock implements Runnable{
@Override
public void run() {
try {
count.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
try {
System.out.println("The "+Thread.currentThread().getName()+" is Working");
}catch (Exception e){
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<THREAD_NUM;i++){
new Thread(new TestLock()).start();
}
Thread.sleep(2000);
for(int i=0;i<THREAD_NUM;i++){
count.countDown();
}
}
附流程解析图:
所有线程都会执行 lock 方法去获取锁
public void lock() {
sync.lock();
}
ReentranLock 默认是 不公平锁 NonfairSync
public ReentrantLock() {
sync = new NonfairSync();
}
所以我们执行的lock方法其实是执行的 ReentranLock 内部类的
NonfairSync 的 lock方法
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
在这里所有的线程都会去 执行
compareAndSetState(0, 1)
但是只有一个线程可以执行 成功,这里我们总共有线程1-线程5 线程1执行成功
并将 AQS的 exclusiveOwnerThread设置为当前线程
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt(); }
acquire会调 tryAcquire方法 再次尝试获取锁,ReentrantLock复写了tryAcquire方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
最终调用的是
ReentrantLock 的nonfairTryAcquire 方法
根据方法可知,如果进入的线程=拥有锁的线程的话state会+1 并返回ture 这就是可重入锁的原理
如果当前线程不是拥有锁的线程就会返回false
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;
}
线程2-线程5执行tryAcquire返回false ,然后就会执行
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
这里我们拆成两部分 首先是addWaiter 在这里会建立CLH队列
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) {//因为第一次进来的时候 tail和head都是null所以这段逻辑并不会进入
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);//线程2 进入
return node;
}
因为线程2进入的时候tail和head还是null 所以会进入enq方法
可以看到这段逻辑采用的是自旋
第一次循环 此时t=tail=null 进入if
if里面 head=tail=new Node();
第二次此时 t=tail=head!=null 进入else
else里 tail=node
t=head t.next=head.next =node=tail
tail.pre=node.pre=t=head
从上面的分析可以看出 执行完 enq方法后 CLH队列如下
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;
}
}
}
}
执行完 addWaiter方法后继续执行 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);
}
}
可以看出这也是一个自旋的方法,我们分解来看
final Node p = node.predecessor(); //node的pre是 head 所以p=head
AQS会再次执行tryAcquire(1)尝试获取锁,但是未成功,接下来会执行
shouldParkAfterFailedAcquire(p, node)
//这个方法是判断再尝试获取锁失败后是否应该挂起当前线程,
如果返回true则执行 parkAndCheckInterrupt 对线程进行LockSuppor.park(this)挂起当前线程
我们来看 进入的参数 pred其实就是head 但是此时head的waitStatus=0 所以会进入else将
head.waitStatus设置为-1(含义就是next节点需要被唤醒)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
回到acquireQueued方法会再次循环 本次因为head.waitStatue=-1
shouldParkAfterFailedAcquire方法返回true 接下来回执行if里面的 parkAndCheckInterrupt
将线程挂起,线程就一直阻塞再这等待被唤醒后执行再自旋
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
线程3-线程5 同样的会和线程2一样执行上面的逻辑执行完后我们可以看到CLH的队列为
------------------------------------------------截止以上都是lock方法执行--------------------------------------------------------------------------------------接下来执行讲解unlock------------------------------------------
首先线程1 会执行 unlock方法 及执行 AQS的release方法
public final boolean release(int arg) {
if (tryRelease(arg)) { //尝试释放锁,Reentrantlock复写了 该方法 就是将state=0 exclusiveOwnerThread=null
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); //唤醒head.next
return true;
}
return false;
}
在这里又会执行 unparkSuccessor方法 唤醒 head.next即 线程2
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
此时线程2 被唤醒 会继续执行 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)) {//这一次会进入if逻辑 并将当前node设置为head
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
线程2 这一次会进入if逻辑 并将当前node设置为head 即此时的CLH队列为
一直到线程5被唤醒
接下来线程5所在node设置为head
此时只剩下head和tail且没有任何关联