读过这篇文章http://ifeve.com/introduce-abstractqueuedsynchronizer/之后感觉对ReentrantLock又有了进一步的认识,但是这篇文章偏于理论,于是我便使用了调试的功能进行了进一步的分析认识,在看这篇文章之前建议先认真阅读上面链接的那篇文章。
先给出里面涉及到的类和接口的关系图:
先贴出来调试的 时候的代码:
public class Run {
public static void main(String[] args) {
final Service s = new Service();
new Thread(new Runnable() {
public void run() {
s.methodA();
}
},"线程A").start();
try {
Thread.sleep(2000); //这里是为了确保线程A沉睡后线程B才开始执行
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
new Thread(new Runnable() {
public void run() {
s.methodB();
}
},"线程B").start();
}
}
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Service {
private ReentrantLock lock = new ReentrantLock(true);//这里研究的是公平锁
private Condition con = lock.newCondition();
private volatile int i = 0;
public void methodA(){
lock.lock(); //step1
try {
lock.lock();//重入锁 step2
try {
System.out.println("1");
Thread.sleep(100000);//step3 沉睡100秒是为了让我们有足够的时间在调试B线程的methodB的时候不会释放锁
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lock.unlock(); //step5
} finally{
lock.unlock(); //step6
}
}
public void methodB(){
lock.lock();//step4 step7
try {
System.out.println("2"); //step8
}finally{
lock.unlock(); //step9
}
}
}
当线程A到达step1的时候,其执行的关键代码:
static final class FairSync extends Sync {
....
final void lock() {
acquire(1);//执行的是Sync的父类AbstractQueuedSynchronizer(后面简称AQS)的方法
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {//step1会进入这里的逻辑,‘获取锁’这三个字的意思我认为就是是否更改了
//state,若更改了就获取到了,若没有更改则没有获取到
//hasQueuedPredecessors()判断队列中除了头结点是否还有其他前驱节点,有则返回true
//if的逻辑 1.当前队列中没有其他线程是当前线程的前驱,2更改state的值为1,
//3设置当前Lock独占线程的属性为当前线程
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {//step2会进入这里的逻辑,重入锁
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;//step4会返回false,没有获取到锁
}
}
}
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
...
public final void acquire(int arg) {
//这里的tryAcquire调用的是FairSync重写的方法
//尝试获取锁,若获取不到,则包装当前线程加入当前队列尾部,并且再次尝试获取锁
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
}
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;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize //若队列为空则new一个头结点并且将tail指向头结点
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {//若队列不为空,则将节点加入队列尾部
t.next = node;
return t;
}
}
}
}
....
当执行完step1的时候,lock的内部的各个属性变化:
state:0->1
exclusiveOwnerThread:null->线程A,其他属性均为初始值。
当执行完step2的时候,lock的内部的各个属性变化:
state:1->2 其他属性均不变。//表示重入锁
当执行到step3的时候线程A沉睡,此时线程B开始执行。
接着会执行step4,执行完step4的时候,lock的内部的属性值:
state:2.
head :new Node[next=tail,thread=null];
tail:new Node(thread=线程B,prev=head);
现在来仔细看看step4发生了什么
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
step4执行的时候tryAcquire返回false,表示没有获取到锁
addWaiter(Node.EXCLUSIVE)会在队列中生成两个节点,并设置成头结点和尾节点,返回尾节点,再看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)) {//若当尾节点的前驱节点为头结点并且获取到锁了
//则将当前节点设置为头结点,这里tryAcquire(arg)会返回false,因为此时的state为2并且
//Lock的(exclusiveOwnerThread)独占线程为线程A,当前线程为线程B
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//step4第一次循环进入shouldParkAfterFailedAcquire时会将尾节点的前驱节
//的waitStatus属性更新值为-1并且返回false,第二次循环进入到
//shouldParkAfterFailedAcquire的时候直接返回true接下来会进入
//parkAndCheckInterrupt
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
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;
}
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);//使得当前线程阻塞,此时线程B就会一直在这里阻塞,直到被unpark.
//step4执行到这里时记为 线程B状态一,后面当线程B被unpark的时候会继续下面的步骤
return Thread.interrupted();
}
分析到这里,也就是step4执行完之后,线程B也就进入阻塞状态了。
此时lock内部的属性状态如下
state:2
head:new Node[waitStatus=-1,next=tail,thread=null,nextWaiter=null] (ps:nextWaiter在条件锁里面会用到,这里没有用到)
tail:new Node[waitStatus=0,pre=head,thread=线程B,nextWaiter=null]
exclusiveOwnerThread:线程A.
其实此时的同步队列中只有两个节点,也就是头结点和尾节点,其内部属性状况如上描述 。
到了线程B状态一后再过一段时间线程A沉睡完了醒来之后,执行到step5
来看看step5执行的关键代码
public void unlock() {
sync.release(1);//
}
//逻辑:1尝试释放锁(与‘获取锁’一样,要改变state的值,但是与其不同的是若想真正的释放成功,还需要修改
//exclusiveOwnerThread所代表的独占线程,这里仅仅是针对ReentrantLock的内部类Sync,其他类型的锁暂不
//清楚),2若释放锁成功,获取头结点,当头结点不为空,并且头结点的waitStatus不为0,会进入
//unparkSuccessor,顾名思义,就是解除后继者的阻塞状态,意思就是把头结点的下一个节点包含的线程unpark
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
先来看下tryRelease,这里的参数releases传入的是1,上面分析过当前的state是为2(表示重入锁)的所以在step5也仅仅只是执行了tryRelease,改变了state的值,由2变为1,返回了false,release也返回fasle
但是当执行到step6的时候此时tryRelease返回了true,lock的内部属性state变为0,exclusiveOwnerThread由线程A变成了null.
接着就是执行释放锁之后的操作了。上面分析过当前的头结点的状态了head:new Node[waitStatus=-1,next=tail,thread=null,nextWaiter=null] ,进入unparkSuccessor(h);
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;
}
紧接着进入unparkSuccessor(Node node),传入的参数:头结点,做了什么:修改了头结点的waitStatus为0,找到头结点的下一个节点,也就是尾节点,其实也就是线程B所在的那个节点,unpark掉,此时线程B的阻塞状态就被解除了。step6执行完之后。lock的内部属性的状态:
state:0
head:Node[waitStatus=0,next=tail,thread=null,nextWaiter=null]
tail:Node[waitStatus=0,pre=head,thread=线程B,nextWaiter=null]
exclusiveOwnerThread:线程A.
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
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);
}
此时回到线程B阻塞的那个地方继续执行,线程B没有执行过interrupt,因此会返回false.
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);//就是这里
return Thread.interrupted();
}
回到这个方法(唉!好麻烦)
接着返回false回到acquireQueued的for循环继续运行
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);
}
}
取到尾节点的前驱节点也就是头结点,再次回到tryAcquire这个方法的时候,state为0,hasQueuedPredecessors很显然返回false,接着就将当前的state修改为1,并将lock的属性exclusiveOwnerThread设置为当前线程,也就是线程B.并返回true,此时已经表明线程B‘获取到锁’了,接着看上面的acquireQueued函数,第一个if条件成立,设置线程B所在的节点为头结点,并且返回false.
到此处step7就执行完成了,并且B线程获得了锁。此时lock的内部状态:
state:1
head=tail=Node[waitStatus=0,pre=null,next=null,thread=线程B,nextWaiter=null].
exclusiveOwnerThread:线程B.
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;
}
}
step8执行业务,
step9进行释放锁.释放锁的过程上面已经分析过了。