尝试阅读ReentrantLock、AbstractQueuedSynchronizer源码(一)

来源:互联网 发布:淘宝靠谱的美国代购 编辑:程序博客网 时间:2024/05/30 22:41

提起ReentrantLock,想必大家最熟悉的就是这lock()unlock()这两个方法了,那今天就从这入手吧!


一、类结构

三个内部类:SyncFairSyncNonfairSync

Sync : 同步器基类
FairSync : 实现公平锁的同步器
NonfairSync : 实现非公平锁的同步器

这里写图片描述

Sync 继承 AbstractQueuedSynchronizerFairSyncNonfairSync继承Sync

二、lock()流程(以公平锁为例)

ReentrantLock lock = new ReentrantLock(true);lock.lock();

当调用lock方法时,当前线程尝试获取锁,下面来看下具体获取过程。

ReentrantLock.FairSync.lock()static final class FairSync extends Sync {        private static final long serialVersionUID = -3000897897090466540L;        final void lock() {            //获取锁            acquire(1);        }       ...    }

调用父类的父类的acquire方法

AbstractQueuedSynchronizer.acquire()public final void acquire(int arg) {        if (!tryAcquire(arg) &&            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))            selfInterrupt();    }

这个acquire方法还是有毫复杂的,流程如下:

1、尝试获取锁(tryAcquire
2、若尝试获取失败,则加入等待队列,并中断自己
3、若尝试获取成功,则占有锁

这里写图片描述

尝试获取锁

ReentrantLock.FairSync.tryAcquire()protected final boolean tryAcquire(int acquires) {            final Thread current = Thread.currentThread();            //c:当前锁是否已被线程拥有            int c = getState();            if (c == 0) {            //1.当前锁未被拥有             //2.当前线程是否是等待队列的第一个,很公平            //3.CAS方式设置state值             //4.已独占的方式获取当前锁                if (!hasQueuedPredecessors() &&                    compareAndSetState(0, acquires)) {                    setExclusiveOwnerThread(current);                    return true;                }            }            //1.当前锁已被拥有            //2.重入锁            //3.设置state值            else if (current == getExclusiveOwnerThread()) {                int nextc = c + acquires;                if (nextc < 0)                    throw new Error("Maximum lock count exceeded");                setState(nextc);                return true;            }            return false;        }

加入等待队列

AbstractQueuedSynchronizer使用双向链表来管理等待队列。结构如下:

这里写图片描述

假如threadA获取了锁,此时threadB尝试获取锁失败,则threadB加入等待队列;

acquireQueued(addWaiter(Node.EXCLUSIVE), arg)

1、新node假如队尾(有尾节点)

AbstractQueuedSynchronizer.addWaiter()private Node addWaiter(Node mode) {        //model表示独占还是共享模式        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;            }        }        //将node节点置为尾节点        enq(node);        return node;    }

这里写图片描述

2、自旋尝试获取锁

threadB被加入等待队列后,不断循环尝试获取锁。

AbstractQueuedSynchronizer.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);        }    }
acquireQueued.shouldParkAfterFailedAcquire()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;    }

当线程尝试获取锁失败后,会调用shouldParkAfterFailedAcquire方法要判断是否阻塞自己。AQS(AbstractQueuedSynchronizer)使用的是CLH队列,不断的轮询前驱节点的状态,根据前驱节点的状态来决定后面的工作。
每一个节点都有使用waitStatus来表示自己的状态;具体值有:
① 1(CANCELLED): 线程取消了竞争锁
② -1 (SIGNAL):当前线程的后继节点需要被唤醒(unpark
③ -2 、-3(下篇文章再细说)
④ 0 :默认值

–> 如果前驱节点的状态为-1,则阻塞当前节点;
–> 如果前驱节点状态为1,则循环删除前驱节点,知道前驱节点的waitStatus<=0
–> 如果前驱节点的状态为其他值,则讲前驱节点的waitStatus赋值为-1

注:对于waitStatus值代表的含义是什么?为什么这样设计?我也挺迷茫的,在后几篇文章中我会尽力的去理解

下面来看看阻塞线程的方法parkAndCheckInterrupt

AbstractQueuedSynchronizer.parkAndCheckInterrupt()private final boolean parkAndCheckInterrupt() {        //阻塞当前线程        LockSupport.park(this);        //返回中断状态,并清空        return Thread.interrupted();}

LockSupport.park/unparkObject类中的wait/notify/notifyAll方法类似,都有阻塞线程的作用;两者之后不能互相唤醒,LockSupport.unpark方法可以指定唤醒某一个线程,能够响应中断,但不会抛出中断异常。

最终threadB在执行完LockSupport.park(this)后,就阻塞了!

三、unlock()流程

ReentrantLock.unlock()    public void unlock() {        sync.release(1);    }
AbstractQueuedSynchronizer.release()//挺简单的public final boolean release(int arg) {        if (tryRelease(arg)) {            Node h = head;            if (h != null && h.waitStatus != 0)                unparkSuccessor(h);            return true;        }        return false;    }
ReentrantLock.tryRelease()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;        }

如果head指向的头结点不为null,且waitStatus !=0,则唤醒后继结点;

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;            //从队未往前查找,找到waitStatus<=0的所有节点中排在最前面的            for (Node t = tail; t != null && t != node; t = t.prev)                if (t.waitStatus <= 0)                    s = t;        }        if (s != null)            LockSupport.unpark(s.thread);    }

唤醒线程以后,被唤醒的线程将从以下代码中继续往前走:

private final boolean parkAndCheckInterrupt() {    LockSupport.park(this); // 刚刚线程被挂起在这里了    return Thread.interrupted();}// 又回到这个方法了:acquireQueued(final Node node, int arg),这个时候,node的前驱是head了

三、未解之谜

1、waitStatus值的具体含义?
2、lock、unlock和中断的关系?

阅读全文
1 0