AQS(获取资源(独占模式)2)--队列同步器

1.获取资源(独占模式)

   acquire(int)

    首先讲解独占模式(Exclusive)下的获取/释放资源过程,其入口方法为:

     public final void acquire(int arg) {

             if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

             selfInterrupt(); }

 

     tryAcquire(arg)为线程获取资源的方法函数,在AQS中定义如下:

     protected boolean tryAcquire(int arg) {

           throw new UnsupportedOperationException();

     }

   注:该方法是空方法,且由protected修饰,说明该方法需要由子类即自定义同步器来实现。

           acquire()方法至少执行一次tryAcquire(arg),若返回true,则acquire直接返回,

           否则进入acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法。

 

    实现的步骤:

         第一步:addWriter()将当前线程加入到等待队列的尾部,并标记为独占模式;

         第一步:acquireQueued()使线程在等待队列中获取资源,直到获取到资源返回,

                       若整个等待过程被中断过,则返回True,否则返回False。

         第三步:如果线程在等待过程中被中断过,则先标记上,待获取到资源后再进行自我中断selfInterrupt(),将中断响应掉。

    涉及的函数:

        1.tryAcquire(int)

           tryAcquire尝试以独占的模式获取资源,如果获取成功则返回True,否则直接返回False,

          默认实现是抛出UnsupportedOperationException,具体实现由自定义扩展了AQS的同步器来完成。

       2.addWaiter(Node)

          addWaiter为当前线程以指定模式创建节点,并将其添加到等待队列的尾部,其源码为:

          private Node addWaiter(Node mode) {

                 Node node = new Node(Thread.currentThread(), mode);

                 // 尝试将节点快速插入等待队列,若失败则执行常规插入(enq方法)

                 Node pred = tail;

                 if (pred != null) {

                    node.prev = pred;

                    if (compareAndSetTail(pred, node)) {

                                   pred.next = node;

                                   return node;

                      }

                  }

              // 常规插入

             enq(node);

             return node;

          }

          再看enq(node)方法:

              private Node enq(final Node node) {

                    for (;;) {

                         Node t = tail;

                          if (t == null) { // Must initialize

                              if (compareAndSetHead(new Node()))

                                      tail = head;

                              } else {

                                   node.prev = t;

                                   if (compareAndSetTail(t, node)) {

                                         t.next = node;

                                         return t;

                                     }

                                }

                           }

                       }

         总结:可以看到,常规插入与快速插入相比:

                第一点:常规插入是自旋过程(for(;;)),能够保证节点插入成功;

                第二点:比快速插入多包含了1种情况,即当前等待队列为空时,需要初始化队列,

                              即将待插入节点设置为头结点,同时为尾节点(因为只有一个嘛)。

          注:常规插入与快速插入均依赖于CAS,其实现依赖于unsafe类,具体代码如下:

                  private final boolean compareAndSetHead(Node update) {

                           return unsafe.compareAndSwapObject(this, headOffset, null, update);

                   }

                  private final boolean compareAndSetTail(Node expect, Node update) {

                          return unsafe.compareAndSwapObject(this, tailOffset, expect, update);

                 }       

                unsafe中的cas操作均是native方法,由计算机CPU的cmpxchg指令来保证其原子性。

 
 学习来源:https://www.jianshu.com/p/0f876ead2846
posted @ 2020-09-01 14:54  小窝蜗  阅读(255)  评论(0)    收藏  举报