ReentrantLock深度解析

这里是引用

源码阅读-ReentrantLock

这个ReentrantLock的实现过程:

1.首先是线程争夺锁资源,获取到后会将自己置为独占线程,方便重入锁的实现

2.当线程没有获取到锁资源时,在acquireQueued(addWaiter(Node.EXCLUSIVE), arg))方法中会把线程封装成一个Node结点,第一个失败线程会将自己加入CLH队列中,头结点是一个空节点(傀儡结点),尾结点存放自己的线程,waitStatus初始值为0,在第二次自旋中会获取自己的前驱结点判断是否为头结点并且尝试获取锁,如果成功就将自己设置为头结点,如果没有成功则需要在下面判断前驱结点的waitStatus是否为signal(需要唤醒后继结点),如果发现前驱结点的waitStatus不是signal则使用CAS将前驱结点改成signal,在下一次自选中将自己park()阻塞。

3.后面的线程进来以后,addWaiter()会把自己加入到队列的尾部,初始的waitStatus为0,在下一次的自旋中判断自己的前驱结点的waitStatus(只有自己前驱结点的status为signal才能够放心的将自己阻塞)会将自己的waitStatus置为singnal,也代表自己需要通知后继结点。

4.acquireQueued(final Node node, int arg)方法中进行尝试获取锁资源,第一次尝试如果自己的prev结点是不是头结点并且tryAcquire()方法获取成功,说明已经成功获取锁资源,那么就将自己置为头结点; 如果判断为假,说明不能获取锁资源,那么就park阻塞,等待自己的前驱结点调用unpark()释放自己。

5.当获取锁资源的线程调用unlock()方法进行释放锁资源时,它是针对head进行一个处理,判断head的waitStatus,如果为signal则唤醒他的后继结点,之后后继结点便可以继续自旋,发现可以获得锁就更替了头结点了,(说明这个锁资源的获取是按照队列的顺序进行获取的,挨个进行)通过调用unparkSuccessor(node)方法实现,这样下个线程就可以继续进行死循环,从而可以直接获取到锁资源。

ReentrantLock(可重入锁)

静态内部类Sync--------继承AQS

 abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        @ReservedStackAccess
     //这个是非公平锁执行的的tryAcquire()方法
        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;
        }
		
     	//实现释放锁资源的方法
        @ReservedStackAccess
        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;
        }

        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // Methods relayed from outer class

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        final boolean isLocked() {
            return getState() != 0;
        }

        /**
         * Reconstitutes the instance from a stream (that is, deserializes it).
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

lock()

    public void lock() {
        //调用内部类Sync(继承AQS)
        //同步资源 + 1 
        sync.acquire(1);
    }

acquire()-------获取锁资源

	//获取同步资源的方法
	public final void acquire(int arg) {
        //尝试获取同步资源。tryAcquire()为真,代表成功获取锁,不必要继续后序操作
        if (!tryAcquire(arg) &&  
            //如果进行到这里,说明没有成功获取锁,需要将线程放入等待队列中
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            
            //if语句为真执行该操作
            selfInterrupt();
    }	

tryAcquire(arg) --------针对于acquire()方法的第一个判断

//非公平锁的tryAcquire()方法实现------------继承内部类Sync
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        protected final boolean tryAcquire(int acquires) {
            //执行下面的nonfairTryAcquire()方法
            return nonfairTryAcquire(acquires);
        }
    }

   final boolean nonfairTryAcquire(int acquires) {
       		//首先获取当前线程对象
            final Thread current = Thread.currentThread();
       		//getState()方法是获取同步状态state的当前值(被volatile修饰,保证可见性)
            int c = getState();
       		//如果state = 0代表锁空闲
            if (c == 0) {
                //这里是一个CAS操作,保证内存值中的state没被修改,那么就将state赋值为acquires的值(默认1)
                if (compareAndSetState(0, acquires)) {
                    //设置当前线程为独占线程,代表持有锁
                    setExclusiveOwnerThread(current);
                    //返回真代表获取锁资源成功
                    return true;
                }
            }
       		//如果当前线程已经被标记成独占线程,那么直接将state + acquires的值(默认为1),即锁重入
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                //设置state的值
                setState(nextc);
                //返回真代表锁重入成功
                return true;
            }
       		//如果都没获取,那么代表获取锁资源失败
            return false;
        }

acquireQueued(addWaiter(Node.EXCLUSIVE), arg))-----------针对acquire()方法的第二个判断

首先我们需要对addWaiter()方法进行剖析(主要作用是用于将节点添加到队尾)

//Node.EXCLUSIVE 为null,即传入为空
private Node addWaiter(Node mode) {
    	//将线程封装成一个Node结点
        Node node = new Node(mode);

    	//死循环保证node必被加入到队列中(配合CAS)
        for (;;) {
            //获取当前队列的尾结点
            Node oldTail = tail;
            //如果存在尾结点继续往后进行
            if (oldTail != null) {
                //设置node的prev指向尾结点                        
                node.setPrevRelaxed(oldTail);
                //使用CAS设置尾结点
                if (compareAndSetTail(oldTail, node)) {
                    //将原尾结点的next指向Node,使得node变为新的尾结点
                    oldTail.next = node;
                    //返回node并(跳出死循环)
                    return node;
                }
            }
            //不存在尾结点,说明队列为空
            else {
                //在下面说明
                initializeSyncQueue();
            }
        }
    }

	//调用该方法说明队列中无结点
    private final void initializeSyncQueue() {
        Node h;
        //使用一个CAS操作设置头结点成功后,设置尾结点
        //这里也可以发现头结点是傀儡结点,没有实际应用,这里把head设置成了null
        if (HEAD.compareAndSet(this, null, (h = new Node())))
            tail = h;
    }

acquireQueued(final Node node, int arg)--------死循环获取锁资源(里面有线程park()方法)

//此时传入的Node结点已经被加入队列中 
final boolean acquireQueued(final Node node, int arg) {
        boolean interrupted = false;
        try {
            for (;;) {
                //node.predecessor()在下面详细解释,主要作用是获取传入的node结点的prev结点(此时的node为尾结点)
                final Node p = node.predecessor();
                //判断p是否为头结点并且尝试获取锁资源
                //从这里可以看出这一是有顺序的获取锁资源的队列
                if (p == head && tryAcquire(arg)) {
                    //如果成功,则说明p结点已经成功获取资源,之后设置头结点为node
                    setHead(node);
                    //将p的强引用 = null,帮助GC
                    p.next = null;
                    
                    return interrupted;
                }
                
                //如果node的prev不是头结点说明队列长度 > 2,下面方法在下面详细解释,主要作用是
                if (shouldParkAfterFailedAcquire(p, node))
                    
                    //这是一个或运算,如果interrupted和parkAndCheckInterrupt()同假为假,其他情况为真
                    //同为假即parkAndCheckInterrupt()返回假,说明线程没有中断点
                    //所以该方法相当于interrupted = parkAndCheckInterrupt()
                    
                    //如果这里为parkAndCheckInterrupt()为真,那么interrupted = true, 说明该线程被中断过了,但是我们的
                    //parkAndCheckInterrupt()判断后就将中断点取消了,那么我们在上面的return interrupted中返回true
                    //在上一层的acquire()的方法中调用selfInterrupt()会再次将自己标记中断点
                    interrupted |= parkAndCheckInterrupt();
            }
        } catch (Throwable t) {
            cancelAcquire(node);
            if (interrupted)
                selfInterrupt();
            throw t;
        }
    }


	//该方法说明head节点是一个空节点,主要作用是next和tail结点进行操作
    private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
    }


	//该方法用于获取传入结点的prev结点
	final Node predecessor() {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
    }


	//此时传入的是node和node的prev结点
	private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        //获取prev结点的等待状态
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            //如果标记为signal,说明他是有义务通知后序结点
            return true;
        if (ws > 0) {
            //说明该节点为取消结点
            
            //这个操作有点类似于,把pred(即node.prev)结点删除操作,让node指向prev的prev结点
            //退出条件是pred的waitStatus属性 <= 0
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
            如果为0,说明是一个新建结点
            如果另一种,说明需要传播
             */
            //这里将他们设置为signal用于通知后序结点
            pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
        }
        return false;
    }


	//挂起并检测是否中断,进入到这里说明waitStatus为signal,即需要通知后继结点
    private final boolean parkAndCheckInterrupt() {
        //挂起线程,调用该方法后就会阻塞在这里,知道被其他线程调用unpark()方法进行唤醒
        LockSupport.park(this);
        //这里是判断是否存在中断点,如果存在即将中断断点删除
        return Thread.interrupted();
    }

unlock()---------释放锁资源

	//释放锁    
    public void unlock() {
            sync.release(1);
    }

tryRelease()方法-------释放锁资源


   public final boolean release(int arg) {
      //尝试释放锁,具体实现在下面
        if (tryRelease(arg)) {
            //如果释放成功,那么就获取头结点(傀儡结点)
            Node h = head;
            //如果头结点的waitStatus不为null和0,如果他为signal,即需要unpark他的后继结点
            if (h != null && h.waitStatus != 0)
                //具体实现解析在下个板块
                unparkSuccessor(h);
            return true;
        }
        return false;
    }	

   protected final boolean tryRelease(int releases) {
       		//考虑到时重入锁,所以需要一次次的释放锁资源
            int c = getState() - releases;
       		//如果线程不是独占线程,则抛出异常(一般情况是不可能抛出异常,除非你先unlock()在lock()前)
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
       		//如果 c = 0 说明锁释放完毕,将独占线程置Null,返回真
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
   }

unparkSuccessor(node)--------------通知后继结点

//此时传入的值为head结点
private void unparkSuccessor(Node node) {
    	//首先获取结点的waitStatus
        int ws = node.waitStatus;
    	//判断状态
        if (ws < 0)
            //因为该节点的锁资源已经被释放,所以waitStatus置0
            node.compareAndSetWaitStatus(ws, 0);
    	//获取头结点的next结点
        Node s = node.next;
   		//如果s(next结点)为null或者被标记为取消结点
        if (s == null || s.waitStatus > 0) {
            //首先将s = null
            s = null;
            //从尾结点p开始获取,如果p的waitStatus<=0(说明没有被取消),则将s = p,之后向前面的prev结点遍历
            //循环的判断条件是 p != node && p != null,说明跳出循环是,p已经到达node或者p = null
            //这个循环可以赋值给s为头结点后的首个waitStatus <= 0的结点
            for (Node p = tail; p != node && p != null; p = p.prev)
                if (p.waitStatus <= 0)
                    s = p;
        }
    	//取得该节点后就将其unpark取消阻塞。
        if (s != null)
            LockSupport.unpark(s.thread);
    }

静态内部类Node

 static final class Node {
        /** 表示节点正在共享模式下等待的标记 */
        static final Node SHARED = new Node();
        /** 标记以指示节点正在独占模式下等待 */
        static final Node EXCLUSIVE = null;

        /** 指示线程已取消的waitStatus值 */
        static final int CANCELLED =  1;
        /** waitStatus值,指示该线程的后继线程自己需要唤醒 */
        static final int SIGNAL    = -1;
        /** waitStatus值指示线程正在等待条件 */
        static final int CONDITION = -2;
        /** 指示下一个acquireShared应无条件传播的waitStatus值 */
        static final int PROPAGATE = -3;

        volatile int waitStatus;

        /**
         * Link to predecessor node that current node/thread relies on
         * for checking waitStatus. Assigned during enqueuing, and nulled
         * out (for sake of GC) only upon dequeuing.  Also, upon
         * cancellation of a predecessor, we short-circuit while
         * finding a non-cancelled one, which will always exist
         * because the head node is never cancelled: A node becomes
         * head only as a result of successful acquire. A
         * cancelled thread never succeeds in acquiring, and a thread only
         * cancels itself, not any other node.
         */
        volatile Node prev;

        /**
         * Link to the successor node that the current node/thread
         * unparks upon release. Assigned during enqueuing, adjusted
         * when bypassing cancelled predecessors, and nulled out (for
         * sake of GC) when dequeued.  The enq operation does not
         * assign next field of a predecessor until after attachment,
         * so seeing a null next field does not necessarily mean that
         * node is at end of queue. However, if a next field appears
         * to be null, we can scan prev's from the tail to
         * double-check.  The next field of cancelled nodes is set to
         * point to the node itself instead of null, to make life
         * easier for isOnSyncQueue.
         */
        volatile Node next;

        /**
         * The thread that enqueued this node.  Initialized on
         * construction and nulled out after use.
         */
        volatile Thread thread;

        /**
         * Link to next node waiting on condition, or the special
         * value SHARED.  Because condition queues are accessed only
         * when holding in exclusive mode, we just need a simple
         * linked queue to hold nodes while they are waiting on
         * conditions. They are then transferred to the queue to
         * re-acquire. And because conditions can only be exclusive,
         * we save a field by using special value to indicate shared
         * mode.
         */
        Node nextWaiter;
     
             Node() {}

        /** Constructor used by addWaiter. */
        Node(Node nextWaiter) {
            //设置nextWaiter
            this.nextWaiter = nextWaiter;
            THREAD.set(this, Thread.currentThread());
        }

        /** Constructor used by addConditionWaiter. */
        Node(int waitStatus) {
            WAITSTATUS.set(this, waitStatus);
            THREAD.set(this, Thread.currentThread());
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值