zoukankan      html  css  js  c++  java
  • AQS共享锁应用之Semaphore原理

    我们调用Semaphore方法时,其实是在间接调用其内部类或AQS方法执行的。Semaphore类结构与ReetrantLock类相似,内部类Sync继承自AQS,然后其子类FairSync和NoFairSync分别实现公平锁和非公平锁的获取锁方法tryAcquireShared(int arg),而释放锁的tryReleaseShared(int arg)方法则有Sync类实现,因为非公平或公平锁的释放过程都是相同的。

    AQS通过state值来控制对共享资源访问的线程数,有线程请求同步状态成功state值减1,若超过共享资源数量获取同步状态失败,则将线程封装共享模式的Node结点加入到同步队列等待。有线程执行完任务释放同步状态后,state值会增加1,同步队列中的线程才有机会获得执行权。公平锁与非公平锁不同在于公平锁申请获取同步状态前都会先判断同步队列中释放存在Node,若有则将当前线程封装成Node结点入队,从而保证按FIFO的方式获取同步状态,而非公平锁则可以直接通过竞争获取线程执行权。

    //Semaphore的acquire()
    public void acquire() throws InterruptedException {
          sync.acquireSharedInterruptibly(1);
      }
    
    /**
    *  注意Sync类继承自AQS
    *  AQS的acquireSharedInterruptibly()方法
    */ 
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        //判断是否中断请求
        if (Thread.interrupted())
            throw new InterruptedException();
        //如果tryAcquireShared(arg)不小于0,则线程获取同步状态成功
        if (tryAcquireShared(arg) < 0)
            //未获取成功加入同步队列等待
            doAcquireSharedInterruptibly(arg);
    }
    //Semaphore中非公平锁NonfairSync的tryAcquireShared()
    protected int tryAcquireShared(int acquires) {
        //调用了父类Sync中的实现方法
        return nonfairTryAcquireShared(acquires);
    }
    
    final int nonfairTryAcquireShared(int acquires) {
             //使用死循环
             for (;;) {
                 int available = getState();
                 int remaining = available - acquires;
                 //判断信号量是否已小于0或者CAS执行是否成功
                 if (remaining < 0 ||
                     compareAndSetState(available, remaining))
                     return remaining;
             }
         }
    private void doAcquireSharedInterruptibly(int arg)
            throws InterruptedException {
         //创建共享模式的结点Node.SHARED,并加入同步队列
       final Node node = addWaiter(Node.SHARED);
         boolean failed = true;
         try {
             //进入自旋操作
             for (;;) {
                 final Node p = node.predecessor();
                 //判断前驱结点是否为head
                 if (p == head) {
                     //尝试获取同步状态
                     int r = tryAcquireShared(arg);
                     //如果r>0 说明获取同步状态成功
                     if (r >= 0) {
                         //将当前线程结点设置为头结点并传播               
                         setHeadAndPropagate(node, r);
                         p.next = null; // help GC
                         failed = false;
                         return;
                     }
                 }
               //调整同步队列中node结点的状态并判断是否应该被挂起
               //并判断是否需要被中断,如果中断直接抛出异常,当前结点请求也就结束
                 if (shouldParkAfterFailedAcquire(p, node) &&
                     parkAndCheckInterrupt())
                     throw new InterruptedException();
             }
         } finally {
             if (failed)
                 //结束该结点线程的请求
                 cancelAcquire(node);
         }
        }
    
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
            //获取当前结点的等待状态
            int ws = pred.waitStatus;
            //如果为等待唤醒(SIGNAL)状态则返回true
            if (ws == Node.SIGNAL)
                return true;
            //如果ws>0 则说明是结束状态,
            //遍历前驱结点直到找到没有结束状态的结点
            if (ws > 0) {
                do {
                    node.prev = pred = pred.prev;
                } while (pred.waitStatus > 0);
                pred.next = node;
            } else {
                //如果ws小于0又不是SIGNAL状态,
                //则将其设置为SIGNAL状态,代表该结点的线程正在等待唤醒。
                compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
            }
            return false;
        }
    
    private final boolean parkAndCheckInterrupt() {
            //将当前线程挂起
            LockSupport.park(this);
            //获取线程中断状态,interrupted()是判断当前中断状态,
            //并非中断线程,因此可能true也可能false,并返回
            return Thread.interrupted();
    }
    
    //不可中的acquireShared()
    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);
                            p.next = null; // help GC
                            if (interrupted)
                                selfInterrupt();
                            failed = false;
                            return;
                        }
                    }
                    if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                        //没有抛出异常中的。。。。
                        interrupted = true;
                }
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
        }
    
     private void setHeadAndPropagate(Node node, int propagate) {
            Node h = head; // Record old head for check below
            setHead(node);//设置为头结点
            /* 
             * 尝试去唤醒队列中的下一个节点,如果满足如下条件: 
             * 调用者明确表示"传递"(propagate > 0), 
             * 或者h.waitStatus为PROPAGATE(被上一个操作设置) 
             * 并且 
             *   下一个节点处于共享模式或者为null。 
             * 
             * 这两项检查中的保守主义可能会导致不必要的唤醒,但只有在有
             * 有在多个线程争取获得/释放同步状态时才会发生,所以大多
             * 数情况下会立马获得需要的信号
             */  
            if (propagate > 0 || h == null || h.waitStatus < 0 ||
                (h = head) == null || h.waitStatus < 0) {
                Node s = node.next;
                if (s == null || s.isShared())
                //唤醒后继节点,因为是共享模式,所以允许多个线程同时获取同步状态
                    doReleaseShared();
            }
        }
    
    //Semaphore的release()
    public void release() {
           sync.releaseShared(1);
    }
    
    //调用到AQS中的releaseShared(int arg) 
    public final boolean releaseShared(int arg) {
           //调用子类Semaphore实现的tryReleaseShared方法尝试释放同步状态
          if (tryReleaseShared(arg)) {
              doReleaseShared();
              return true;
          }
          return false;
      }
    
    //在Semaphore的内部类Sync中实现的
    protected final boolean tryReleaseShared(int releases) {
           for (;;) {
                  //获取当前state
                 int current = getState();
                 //释放状态state增加releases
                 int next = current + releases;
                 if (next < current) // overflow
                     throw new Error("Maximum permit count exceeded");
                  //通过CAS更新state的值
                 if (compareAndSetState(current, next))
                     return true;
             }
            }
    
    private void doReleaseShared() {
        /* 
         * 保证释放动作(向同步等待队列尾部)传递,即使没有其他正在进行的  
         * 请求或释放动作。如果头节点的后继节点需要唤醒,那么执行唤醒  
         * 动作;如果不需要,将头结点的等待状态设置为PROPAGATE保证   
         * 唤醒传递。另外,为了防止过程中有新节点进入(队列),这里必  
         * 需做循环,所以,和其他unparkSuccessor方法使用方式不一样  
         * 的是,如果(头结点)等待状态设置失败,重新检测。 
         */  
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                // 获取头节点对应的线程的状态
                int ws = h.waitStatus;
                // 如果头节点对应的线程是SIGNAL状态,则意味着头
                //结点的后继结点所对应的线程需要被unpark唤醒。
                if (ws == Node.SIGNAL) {
                    // 修改头结点对应的线程状态设置为0。失败的话,则继续循环。
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;
                    // 唤醒头结点h的后继结点所对应的线程
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            // 如果头结点发生变化,则继续循环。否则,退出循环。
            if (h == head)                   // loop if head changed
                break;
        }
    }
    
    
    //唤醒传入结点的后继结点对应的线程
    private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
          if (ws < 0)
              compareAndSetWaitStatus(node, ws, 0);
           //拿到后继结点
          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);
        }

     剖析基于并发AQS的共享锁的实现(基于信号量Semaphore)

  • 相关阅读:
    python函数内容
    python读写csv文件
    python正则表达式
    python使用MYSQL数据库
    python简单面试题
    python执行cmd命令
    python详解json模块
    我的自动化测试之路
    测试开发这一年
    招聘测试人员,我在面试什么?
  • 原文地址:https://www.cnblogs.com/doit8791/p/9163634.html
Copyright © 2011-2022 走看看