java.util.concurrent学习(七) AbstractQueuedSynchronizer

AbstractQueuedSynchronizer框架

 

AbstractOwnableSynchronizer

AbstractOwnableSynchronizer是一个线程持有者同步器,它定义了一个transient变量Thread exclusiveOwnerThread来表示一个排他的线程,仅提供了设置和获取该变量的方法。通过设置exclusiveOwnerThread来表示同步器被一个排他的线程所持有。juc中AbstractQueuedSynchronizer和AbstractQueuedLongSynchronizer继承了该类。

AbstractQueuedSynchronizer

AbstractQueuedSynchronizer是juc中一个十分重要的同步器,从下面这个图中我们可以看到ReentrantLock,ThreadPoolExecutor等一些比较常用的工具都是通过他来实现的。了解AbstractQueuedSynchronizer能帮助我们快速理解juc的其他工具。

作为一个同步器,AbstractQueuedSynchronizer同时提供了共享锁和独享锁的实现,AbstractQueuedSynchronizer通过内部类Node和ConditionObject来维护两个队列,队列的是由一个个Node组成,不熟悉Node的同学可以参考笔者的另一篇文章java.util.concurrent学习(七) AbstractQueuedSynchronizer 内部类Node ,ConditionObject,熟悉这两个内部类以后我们来看一下AbstractQueuedSynchronizer的两个队列:

API:

方法描述
acquire(int arg)
获取排他锁
acquireInterruptibly(int arg)
可打断式的获取排他锁,即在获取排他锁的自旋过程中,一旦线程被打断,则抛出
InterruptedException,停止获取锁。
tryAcquireNanos(int arg, long nanosTimeout)
可打断式的,可限制获取锁的超时时间的获取排他锁。
release(int arg)
释放排他锁,唤醒头部的下一个线程。
acquireShared(int arg)
获取共享锁
acquireSharedInterruptibly(int arg)

可打断式的获取共享锁

tryAcquireSharedNanos(int arg, long nanosTimeout)
可打断式的,可限制获取锁的超时时间的获取共享锁。
releaseShared(int arg)
释放共享锁
hasQueuedThreads()
判断队列中是否仍有线程
 hasContended()

查询是否有线程曾试图获取此同步器;也就是说,如果一个获取方法被阻塞了。

getFirstQueuedThread()
获取等待时间最长的线程
isQueued(Thread thread)
判断线程是否在队列中
hasQueuedPredecessors()
判断是否有等待时间更长的线程
getQueueLength()
获取队列长度
getQueuedThreads()
获取队列中所有线程的集合
getExclusiveQueuedThreads()
获取队列中所有排他线程的集合
getSharedQueuedThreads()
获取队列中所有共享线程的集合
owns(ConditionObject condition)
判断参数中的condition是否依赖于当前同步器

重点说一下获取锁的api:

1.acquire(int arg) 获取排他锁

  public final void acquire(int arg) {
//如果获取不到排他锁,同时加入队列获取排他锁也失败
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//则打断当前线程
            selfInterrupt();
    }

/**
*添加节点
*/
 private Node addWaiter(Node mode) {
       //创建一个当前线程的节点,指定模式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(final Node node)追加到队尾并返回
        enq(node);
        return node;
    }

//追加队列
private Node enq(final Node node) {
        //自旋
        for (;;) {
            Node t = tail;
            //当队尾节点为空时,初始化头节点和尾节点,此时头尾节点相同
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
           //当队尾节点存在时,将node追加到队尾并返回
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

//在队列中获取锁
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)) {
                 //如果获取到了锁,则将当前节点置为头节点,并返回false
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                 //如果前驱节点不是头节点 或者没有获取到锁
                 //则判断node节点在获取排他锁失败后是否需要阻塞线程
                 //同时阻塞并返回线程是否已打断
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
             //如果无法获取到锁,则取消该节点
                cancelAcquire(node);
        }
    }

//判断node节点在获取排他锁失败后是否需要阻塞线程
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
           //如果前驱节点的状态为SIGNAL,则返回true
            return true;
  
        if (ws > 0) {
            //如果前驱节点的状态为取消,则不断获取前驱节点,并重置node的前驱节点
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
          //如果前驱节点的状态为0或者-3,则将前去节点的状态改为sinal
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
//阻塞当前线程,并返回线程是否已打断
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

//取消一个节点
private void cancelAcquire(Node node) {
        //如果节点为空,则忽略
        if (node == null)
            return;
        //将节点线程置为null
        node.thread = null;

        //跳过已取消的前驱节点
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;

        //获取后继节点
        Node predNext = pred.next;

       //将节点状态置为取消
        node.waitStatus = Node.CANCELLED;

        //如果该节点为队尾节点,则将前驱节点置为队尾
        if (node == tail && compareAndSetTail(node, pred)) {
            //将前驱结点的后继节点置为null
            compareAndSetNext(pred, predNext, null);
        } else {
           
            int ws;
           //如果前驱节点不是头节点,同时前驱节点的状态是signal或者将前驱节点从<0置为signal,同                          //时前驱结点的线程不为null
            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);
            }
           //该节点的后继节点指向自己,帮助gc快速回收
            node.next = node; // help GC
        }
    }

//打断当前线程
    static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }

看懂代码以后,我们用流程图来梳理一下获取排他锁的流程:

2.获取共享锁 acquireShared(int arg)

    public final void acquireShared(int arg) {
         //尝试获取共享锁
        if (tryAcquireShared(arg) < 0)
           //如果失败,则尝试不间断的获取共享锁
            doAcquireShared(arg);
    }

//不间断的获取共享锁
private void doAcquireShared(int arg) {
        //创建一个共享节点并添加到队尾
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
             //自旋
            for (;;) {
                  //获取前驱结点
                final Node p = node.predecessor();
                 //如果前驱结点是头节点
                if (p == head) {
                    //尝试获取共享锁
                    int r = tryAcquireShared(arg);
                    //如果获取成功
                    if (r >= 0) {
                         设置头节点并传播
                        setHeadAndPropagate(node, r);
                        //前驱结点的后继节点指控,帮助gc快速回收
                        p.next = null; // help GC
                        //如果interrupted 为true,则打断当前线程,并退出自旋
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                //检查获取失败后是否需要阻塞
                if (shouldParkAfterFailedAcquire(p, node) &&
                   //如果需要,则阻塞该线程并判断是否被打断
                    parkAndCheckInterrupt())
                  //如果打断则修改局部变量interrupted 
                    interrupted = true;
            }
        } finally {
             //finally,如果获取失败,则取消该节点
            if (failed)
                cancelAcquire(node);
        }
    }
//设置头节点,并传播
private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; 
        //将该节点置为头节点
        setHead(node);

        //当以下任意一种情况出现时
        //1.propagate 获取共享锁结果大于零
        //2.头节点不存在
        //3.头节点状态<0(signal,condition,propagate)
        //4.新的头节点不存在
        //5.新的头节点状态<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();
        }
    }

private void doReleaseShared() {
       //自旋
        for (;;) {
            //获取头部节点
            Node h = head;
            //如果队列存在
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                  //如果头节点的状态是signal
                if (ws == Node.SIGNAL) {
                    //将头节点从signa改为0,如果失败则重新开始检查
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;           
                     //释放后继节点
                    unparkSuccessor(h);
                }
                //如果头节点的状态是0
                else if (ws == 0 &&
                        //则将其状态改为PROPAGATE,如果失败则重新开始
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;               
            }
            //如果头节点已改,则重新开始检查
            if (h == head)                   
                break;
        }
    }

private void unparkSuccessor(Node node) {
     
        int ws = node.waitStatus;
        if (ws < 0)
          //如果节点状态是负的,将状态改为0
            compareAndSetWaitStatus(node, ws, 0);


        //获取后继节点
        Node s = node.next;
       //如果后继节点不存在或者状态小于0
        if (s == null || s.waitStatus > 0) {
            s = null;
            //从队尾开始不断向前获取不为空的节点直到该节点(但不包含该节点),将s指向该节点后最//近的一个非取消的节点
            for (Node t = tail; t != null && t != node; t = t.prev)
        
                if (t.waitStatus <= 0)
            
                    s = t;
        }
     //如果s节点存在
        if (s != null)
           //则唤醒s节点的线程
            LockSupport.unpark(s.thread);
    }

流程图:

参考:

AbstractQueuedSynchronizer源码解读

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值