ReentrantLock源码学习总结 (二)

ReentrantLock 示例

    private ReentrantLock lock = new ReentrantLock(true);

    public void f(){
        try {
            lock.lock();
            //do something
        }
        finally {
            lock.unlock();
        }
    }

源码解析(公平锁-unlock流程)

ReentrantLock#unlock()

	public void unlock() {
        sync.release(1);
    }

AbstractQueuedSynchronizer#release(int arg)

 	public final boolean release(int arg) {
        //尝试释放锁,只有当 state == 0 才会释放成功
        if (tryRelease(arg)) {
            //释放成功之后,查看head节点,如果head节点不为 null,并且,head节点不为0 (可能为 -1)
            Node h = head;
            if (h != null && h.waitStatus != 0)
                //unpark 线程
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

ReentrantLock.Sync#tryRelease(int arg)

  protected final boolean tryRelease(int releases) {
      		//state - releases,上锁之后,state > 0,所以释放锁要减掉
            int c = getState() - releases;
      		//如果当前线程并不是持有锁的线程,不能乱释放锁,直接给你抛个异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
        	//释放标志
            boolean free = false;
      		//由于是可重入锁,所以当 c == 0 的情况下才真正释放完成
            if (c == 0) {
                free = true;
                //设置当前线程为空
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

AbstractQueuedSynchronizer#unparkSuccessor(Node node)

	private void unparkSuccessor(Node node) {
        //获取head node节点的 waitStatus , < 0(-1) 就CAS 改回 0
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
		//找到下一个节点
        Node s = node.next;
        //如果下一个节点为null,或者 已经取消抢锁了,继续往下找
        if (s == null || s.waitStatus > 0) {
            s = null;
            //从尾部向前遍历,如果 waitStatus<=0 就赋值给 s,这样保证最前边的一个waitStatus<=0的线程可以唤醒。
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        //如果下一个节点不为 null,直接 unpark 线程,对应代码就是AbstractQueuedSynchronizer#acquireQueued(Node node, int arg) 中 的for(;;),这样线程唤醒之后,就会尝试再去获取锁了。
        if (s != null)
            LockSupport.unpark(s.thread);
    }

总结

释放锁的流程比较简单,就是讲 state减去1,然后去队列里面找下一个节点,最后执行 unpark 方法唤醒线程。

posted @ 2019-10-17 09:46  丶Pz  阅读(214)  评论(0编辑  收藏  举报