ReentrantLock实现的源码分析

读过这篇文章http://ifeve.com/introduce-abstractqueuedsynchronizer/之后感觉对ReentrantLock又有了进一步的认识,但是这篇文章偏于理论,于是我便使用了调试的功能进行了进一步的分析认识,在看这篇文章之前建议先认真阅读上面链接的那篇文章。
先给出里面涉及到的类和接口的关系图:
ReentrantLock实现的源码分析_第1张图片

先贴出来调试的 时候的代码:

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进行释放锁.释放锁的过程上面已经分析过了。

下面是这个过程中的流程图
ReentrantLock实现的源码分析_第2张图片

你可能感兴趣的:(多线程进阶,多线程,源码)