当先锋百科网

首页 1 2 3 4 5 6 7

void acquire(int arg):独占式获取同步状态,如果获取失败则将当前现场插入同步队列进行等待。
          1)调用tryAcquire使用CAS尝试再次获取同步状态,若成功方法直接返回,把当前线程置为持有锁线程;
          若再次尝试失败,调用addWaiter()
          2)剖析源码:将当前线程封装为Node节点后尾插入同步队列
           private Node addWaiter(Node mode) {
           //将当前线程以指定的模式封装为节点
                    Node node = new Node(Thread.currentThread(), mode);
                    // 拿到当前同队列的尾节点
                    Node pred = tail;
                    if (pred != null) {
                        node.prev = pred;
                        //CAS将当前节点尾插入同步队列
                        if (compareAndSetTail(pred, node)) {
                            pred.next = node;
                            return node;
                        }
                    }
                    //当前队列为空或者CAS失败时会调用enq方法
                    enq(node);
                    return node;
                }
//当同步队列为空时,完成队列初始化操作以及不断进行CAS操作将当前节点尾插入同步队列
                 private Node enq(final Node node) {
                 //死循环-不断自旋
                        for (;;) {
                        //拿到尾节点
                            Node t = tail;
                            //当前队列为空
                            if (t == null) {
                            //完成队列初始化操作,头结点中不放数据,只是作为起始标记,lazy-load,在第一次用的时候new
                                if (compareAndSetHead(new Node()))
                                    tail = head;
                            } else {
                                node.prev = t;
                                //不断将当前节点使用CAS尾插入队列中直到成功为止
                                if (compareAndSetTail(t, node)) {
                                    t.next = node;
                                    return t;
                                }
                            }
                        }
                    }


               //final修饰,子类只能用,不能修改
                 final boolean acquireQueued(final Node node, int arg) {
                 //设置失败状态,初始化为true
                      boolean failed = true;
                          try {
                          //设置中断状态,默认为false,
                             boolean interrupted = false;
                             //不断自旋
                                for (;;) {
                                //拿到当前节点前驱节点
                                    final Node p = node.predecessor();
                                    //当前节点前驱节点为头结点并且再次获取同步状态成功
                                    if (p == head && tryAcquire(arg)) {
                                    //将当前节点置为头结点
                                        setHead(node);
                                        //将前驱节点出队
                                        p.next = null;
                                        failed = false;
                                        return interrupted;
                                    }
                                    if (shouldParkAfterFailedAcquire(p, node) &&
                                        parkAndCheckInterrupt())
                                        interrupted = true;
                                }
                   } finally {
                                if (failed)
                                //将当前节点设置为取消状态;取消状态设置为1
                                    cancelAcquire(node);
                       }
               }

             节点从同步队列获取同步状态的前提:
             只有当前驱节点为头结点时,线程才有机会获取同步状态

前驱节点不是头结点或者获取同步状态失败时:
尝试将前驱节点状态改为 Node.SIGNAL,表示此时当前节点应该被阻塞
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
       //获取前驱节点状态
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
          //表示应该将当前节点阻塞
            return true;
            //前驱节点被取消了
        if (ws > 0) {
        //一直向前找节点状态不是取消状态的前驱节点
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            //将前驱节点状态置为-1
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

    获取锁失败等待竞争锁的队列是同步队列
    等待被唤醒的队列是等待队列,等待队列没有被唤醒就不存在竞争。