AbstractQueuedSynchronizer

一、AQS是构建同步组件的基础

AQS以模板方式在内部定义了获取锁和释放同步状态的模板方法,并留下钩子函数供子类继承时进行扩展。钩子函数:tryAcquire(int arg)、tryRelease(int arg)、tryAcquireShared(int arg)、tryReleaseShared(int arg)

二、同步等待队列

AQS维护了一个同步等待队列,是一个双向链表;

//队列的头,懒加载,除了初始化,修改只有setHead方法,如果头存在,waitStatus不可能是CANCELLED
private transient volatile Node head;
//队列尾 懒加载,修改只有enq方法,增加新的节点
private transient volatile Node tail;

Node是AQS内部的一个静态内部类

/*下面是waitStatus*/
//表示线程等待超时或中断,取消继续等待
static final int CANCELLED =  1;
//表示线程释放锁后需要唤醒后续节点
static final int SIGNAL    = -1;
//是不是使用condition的await
static final int CONDITION = -2;
//
static final int PROPAGATE = -3;

主要属性

//表示节点线程的状态
volatile int waitStatus;
//指向前置节点
volatile Node prev;
//后置节点
volatile Node next;
//当前节点的线程
volatile Thread thread;

Node nextWaiter;

三、独占模式源码解读

1、acquire(int i)

此方法是独占模式下线程获取锁的顶层入口;如果获取到锁,立即返回,否则进入等待队列;直到获取到资源为止,整个过程忽略中断

    //获取独占锁
    public final void acquire(int arg) {
        //tryAcquire 是钩子函数需要子类实现,如果尝试获取失败,则加入等待队列
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

函数流程:

  1. tryAcquire尝试获取锁(需要子类自己实现),如果成功直接返回
  2. 如果失败,addWaiter会将该线程加入等待队列,并标记为独占模式
  3. acquireQueued()使线程在等待队列中获取资源,一直获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
  4. 如果线程在等待过程中被中断,他是不响应的,只有在获取到资源的时候,才进行自我中断
2、tryAcquire(int)

tryAcquire()尝试获取锁,无论成功还是失败都会立即返回

    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

AQS只是一个框架,具体看自定义同步器是如何实现,不使用抽象修饰,是因为对于独占只需实现tryAcquire-tryRelease,共享锁只需实现tryAcquireShared-tryReleaseShared

3、addWaiter(Node)

addWaiter方法用于将当前线程加入等待队列,并返回当前线程所在节点


    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // 尝试快速放入队尾
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //上一步失败通过enq放入队尾
        enq(node);
        return node;
    }
    
    private Node enq(final Node node) {
        //CAS加自旋,直到加入队尾
        for (;;) {
            Node t = tail;
            if (t == null) { //  tail=null表示队列还没有初始化,需要进行初始化操作
                // 创建一个空节点为head并且tail也指向它
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

从以上加入队列可以看出,头结点为一个空节点

4、acquireQueued(Node,int)

当前节点是获取资源失败,已经放入等待队列;自旋获取锁,知道获取锁成功,并返回中断状态


final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            //标记等待过程中是否被中断
            boolean interrupted = false;
            for (;;) {
                //获取前置节点
                final Node p = node.predecessor();
                //前置节点为head的时候,才可以尝试获取资源
                //(head为空节点,此时当前节点为实际头结点)
                if (p == head && tryAcquire(arg)) {
                    //获取锁成功后, 将当前节点设置为头结点,并修改为空节点
                    setHead(node);
                    // 原来的头结点移除引用
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //是否需要进入waiting状态,知道被unpark
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
            	//当子类实现的tryAcquire方法抛出异常时,取消锁获取
                cancelAcquire(node);
        }
    }
4.1、shouldParkAfterFailedAcquire

这个是很重要的方法,获取锁失败后寻找安全点休眠
如果前置节点正常,则返回true,进入parkAndCheckInterrupt休眠;否则再回到acquireQueued方法中的自旋直至获取锁或者异常或者返回true


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.
             */
             //指后续节点需要park
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
             // 前置节点取消了,就继续向前找,直到找到最近的一个正常状态,并放在它后面;
             //  那些被放弃的节点,由于自己的加塞,他们户会形成一个无用的链,GC会回收
            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.
             */
             //如果前置节点正常,就设置状态为SIGNAL,当前也可能失败
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
4.2、parkAndCheckInterrupt()

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);//调用park使线程进入waiting
        return Thread.interrupted();//唤醒后返回中断状态
    }

acquireQueued()流程

  1. 节点进入队尾后检查状态,前置节点是否为head,是否能获取资源,不能获取就找到安全点waiting
  2. 调用park()进入waiting,等待unpark或interrupt唤醒
  3. 被唤醒后查看自己是否有资格获取资源,如果拿到,head指向当前节点,返回等待过程中是否被中断的标志;如果没有拿到,1重新开始
5、release(int)

独占模式下释放资源的顶层入口;彻底释放后,负责唤醒等待队列里的其他线程获取资源


public final boolean release(int arg) {
        //  自定义实现的释放方法
        if (tryRelease(arg)) {
            Node h = head;
            // 如果头结点为null说明没有等待队列,无需唤醒;
            // 如果头结点的状态为0,说明头节点初始化后,第二位节点正在寻找安全位置时,无需唤醒
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);// 唤醒等待队列的下一个节点
            return true;
        }
        return false;
    }
5.1、unparkSuccessor(Node)

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) // 设置头结点status为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);
    }

四、共享模式源码解读

1、acquireShared(int)
1、1 doAcquireShared(int)

获取共享锁的主要方法:
加入队列并返回节点,如果当前节点的前置节点为head便尝试获取锁,如果不是则寻找安全位置休眠


    private void doAcquireShared(int arg) {
        //addWaiter方法用于将当前线程加入等待队列,并返回当前线程所在节点
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                //获取前置节点
                final Node p = node.predecessor();
                if (p == head) {
                    //前置节点为head,表示有资格去尝试获取锁
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        //>=0表示获取锁成功
                        //设置当前节点为头节点,并且向后传播
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            //如果在等待过程中断过,在这将中断补上
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                //与独占锁一致,寻找安全点waiting,等待被唤醒或者中断
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
1.2 setHeadAndPropagate(Node,int)

// 共享锁获取成功后设置头结点,标记

private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);//设置当前节点为头节点
        //如果还有剩余量,则唤醒下一个共享线程
        // 不论是老的头结点还是新的只要waitStatus <0 都会唤醒后继节点
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            // 如果当前节点的后继节点是共享类型或者没有后继节点,则进行唤醒
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }
1.3 doReleaseShared()

private void doReleaseShared() {
    for (;;) {
        Node h = head;
        // 对于队列存在元素才进行唤醒
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            //表示后继节点需要被唤醒
            if (ws == Node.SIGNAL) {
                //设置头结点状态为0,防止唤醒两次
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                unparkSuccessor(h);//唤醒下一个节点
            }
            //如果后继节点暂时不需要唤醒,则把当前节点状态设置为PROPAGATE确保以后可以传递下去
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        // 如果头结点没有变化则结束
        // 如果有其他线程获取了锁,则需要自旋继续唤醒
        if (h == head)                   // loop if head changed
            break;
    }
 }

五、公用源码

1、cancelAcquire(Node)

取消继续获取锁


    private void cancelAcquire(Node node) {
        // Ignore if node doesn't exist
        if (node == null)
            return;

        node.thread = null;

        // 跳过已取消的前置任务,找到正常前置节点
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;

        // 正常前置节点的后置节点,以便使用CAS修改
        Node predNext = pred.next;

        // 设置当前节点的状态为1,其他操作会忽略当前节点
        node.waitStatus = Node.CANCELLED;

        // 如果当前节点是尾节点,那么前置正常节点需要成为尾节点
        if (node == tail && compareAndSetTail(node, pred)) {
            // 成为尾节点,需要将其后置节点设置为null
            compareAndSetNext(pred, predNext, null);
        } else {
            // If successor needs signal, try to set pred's next-link
            // so it will get one. Otherwise wake it up to propagate.
            int ws;
            // 如果前置不是头结点,并且状态不是SIGNAL 设置为SIGNAL,持有线程
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
                Node next = node.next;
                if (next != null && next.waitStatus <= 0)
                    //将前置节点的后置节点设置为当前节点的后置节点
                    compareAndSetNext(pred, predNext, next);
            } else {
                unparkSuccessor(node);
            }

            node.next = node; // help GC
        }
    }

六、ConditionObject

基础属性


        /** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;
1、await()

此方法是响应中断的,调用此方法会释放当前获取的锁,直到被唤醒

添加一个等待者进入队列

//由于是独占锁已被获取,所以不会出现线程安全问题
public final void await() throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    //加入队列
    Node node = addConditionWaiter();
    //释放当前线程的锁
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    // 判断节点是否在同步队列中
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);//等待
        //等待被唤醒后,检查中断,并尝试降入同步队列
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    // acquireQueued来尝试获取锁,或寻找安全点
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        // 是自我中断还是抛出中断异常
        reportInterruptAfterWait(interruptMode);
}
1、1addConditionWaiter()

private Node addConditionWaiter() {
    Node t = lastWaiter;
    // If lastWaiter is cancelled, clean out.
    if (t != null && t.waitStatus != Node.CONDITION) {
        //移除被取消的waiter
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
    // 为当前线程创建一个节点
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
    lastWaiter = node;
    return node;
}
1.1.1、unlinkCancelledWaiters
private void unlinkCancelledWaiters() {
    Node t = firstWaiter;
    //用于临时存放遍历到的正常节点,以便指向下一个正常节点
    Node trail = null;
    while (t != null) {
        //获取下一个节点
        Node next = t.nextWaiter;
        if (t.waitStatus != Node.CONDITION) {
            t.nextWaiter = null;
            if (trail == null)
                //之前没有正常节点,首节点就是当前节点的next
                firstWaiter = next;
            else
                // 存在正常节点,它的next指向,当前废弃节点的next
                trail.nextWaiter = next;
            if (next == null)
                //如果next为null,trail可能是正常节点也可能是null
                lastWaiter = trail;
        }
        else
            trail = t;
        t = next;
    }
}
1.2、fullyRelease

释放当前线程获取的锁


final int fullyRelease(Node node) {
    boolean failed = true;
    try {
        int savedState = getState();
        if (release(savedState)) {
            failed = false;
            return savedState;
        } else {
            throw new IllegalMonitorStateException();
        }
    } finally {
        if (failed)
            node.waitStatus = Node.CANCELLED;
    }
}
1.3、isOnSyncQueue(node)

final boolean isOnSyncQueue(Node node) {
    if (node.waitStatus == Node.CONDITION || node.prev == null)
        return false;
    if (node.next != null) // If has successor, it must be on queue
        return true;
    //在同步队列中查找当前节点
    return findNodeFromTail(node);
}

private boolean findNodeFromTail(Node node) {
        Node t = tail;
        for (;;) {
            if (t == node)
                return true;
            if (t == null)
                return false;
            t = t.prev;
        }
    }
1.4

private int checkInterruptWhileWaiting(Node node) {
    return Thread.interrupted() ?
        (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
        0;
}

final boolean transferAfterCancelledWait(Node node) {
        // 如果没有被中断就尝试修改状态为0
        if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
           //加入同步队列
           enq(node);
            return true;
        }
      
        while (!isOnSyncQueue(node))
            Thread.yield();
        return false;
    }
2、signal

唤醒await中的线程


public final void signal() {
    if (!isHeldExclusively())//不是持有锁的线程抛出异常
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        //唤醒头节点
        doSignal(first);
}

private void doSignal(Node first) {
    do {
        if ( (firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;
        first.nextWaiter = null;
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}

final boolean transferForSignal(Node node) {
    /*
     * If cannot change waitStatus, the node has been cancelled.
     */
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;

   // 将节点加入同步队列
    Node p = enq(node);
    int ws = p.waitStatus;
    //如果节点被取消或者设置SIGNAL状态失败,就唤醒进行重新同步
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}

private void doSignalAll(Node first) {
    lastWaiter = firstWaiter = null;
    do {
        Node next = first.nextWaiter;
        first.nextWaiter = null;
        transferForSignal(first);
        first = next;
    } while (first != null);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值