关于AbstractQueuedSynchronizer部分方法的注释和理解

  • CANCELLED:值为1,在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消该Node的结点,其结点的waitStatus为CANCELLED,即结束状态,进入该状态后的结点将不会再变化。

  • SIGNAL:值为-1,被标识为该等待唤醒状态的后继结点,当其前继结点的线程释放了同步锁或被取消,将会通知该后继结点的线程执行。说白了,就是处于唤醒状态,只要前继结点释放锁,就会通知标识为SIGNAL状态的后继结点的线程执行。

  • CONDITION:值为-2,与Condition相关,该标识的结点处于等待队列中,结点的线程等待在Condition上,当其他线程调用了Condition的signal()方法后,CONDITION状态的结点将从等待队列转移到同步队列中,等待获取同步锁。

  • PROPAGATE:值为-3,与共享模式相关,在共享模式中,该状态标识结点的线程处于可运行状态。

  • 0状态:值为0,代表初始化状态。

1、acquire(int arg)  独占式获取同步状态,会调用重写的tryAcquire(int arg)方法

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
//tryAcquire()自定义同步器获取同步状态的实现方法

2、addWaiter(Node mode)   如果同步状态获取失败,则构造同步节点(Node.EXCLUSIVE),并通过addWaiter(Node node)方法将改节点加入到同步队列尾部

private Node addWaiter(Node mode) {
        //以给定模式构造节点。mode有两种:EXCLUSIVE(独占)和SHARED(共享)
        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;
    }

3、enq()  尾部快速添加不成功,则再次利用“死循环”的方式将节点添加到同步队列尾部。

//CAS"自旋",直到成功加入队尾
private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            // 队列为空,创建一个空的标志结点作为head结点,并将tail也指向它。
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    //将tail指向head节点
                    tail = head;
            } else {
                //1.将新节点的前置指针指向链表尾部节点
                node.prev = t;
                //2.通过CAS将tail的引用指向node
                if (compareAndSetTail(t, node)) {
                    //3.将原尾部节点的后置指针指向新节点
                    t.next = node;
                    //for循环出口
                    return t;
                }
            }
        }
    }

4、acquireQueued(Node node,int arg) 使该节点以“死循环”的方式获取不同状态。

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true; //标记是否成功拿到资源
        try {
            boolean interrupted = false; //标记等待过程中是否被中断过
            for (;;) {
                final Node p = node.predecessor(); //拿到前驱节点
                //1.如果前驱是head,即该结点已成第二节点,
                //2.那么便有资格去尝试获取资源(tryAcquire(arg))。
                if (p == head && tryAcquire(arg)) {
                    //获取同步状态成功,指定为新的head节点
                    setHead(node);
                    //切断原head与node节点队列的链接
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

5、shouldParkAfterFailedAcquire(Node, Node)

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            //值为-1,后继节点的线程处于等待状态,而当前节点的线程如果释放了同步状态或者被取消,将会通知后继节点,是后继节点的线程得意运行
            return true;
        if (ws > 0) {
            /*
             * 前驱节点被取消(CANCELLED),一直往前找,找到最近的一个未被取消(CANCELLED)的节点
             * 并放弃那些被跳过的节点。随后被放弃的节点会被GC。
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            //如果前驱正常,那就把前驱的状态设置成SIGNAL
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

6、parkAndCheckInterrupt()

private final boolean parkAndCheckInterrupt() {
     LockSupport.park(this);//调用park()使线程进入waiting状态
     return Thread.interrupted();//如果被唤醒,查看自己是不是被中断的。
}

总结acquireQueued(Node node,int arg) 该函数的具体流程:

  1. 结点进入队尾后,检查状态,找到安全休息点;
  2. 调用park()进入waiting状态,等待unpark()或interrupt()唤醒自己;
  3. 被唤醒后,看自己是不是有资格获取同步状态。如果获取了同步状态,head指向当前结点,并返回从入队到获取同步状态的整个过程中是否被中断过;如果没拿到,继续流程1。

总结acquire(int arg)的流程

  1. 调用自定义同步器的tryAcquire()尝试直接去获取同步状态,如果成功则直接返回;
  2. 没成功,则addWaiter()将该线程生成节点(Node)加入等待队列的尾部,并标记为独占模式;
  3. acquireQueued()使线程在等待队列中休息,等待前驱节点调用release(int arg)方法释放资源并且唤醒(unpark())其后继节点。后继节点会去尝试获取资源。获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
  4. 如果线程在等待过程中被中断过,它是不响应的;只是获取资源后才再进行自我中断selfInterrupt(),将中断补上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值