美文网首页
看!源码之AQS中断设计与实现(内涵jvm部分实现)

看!源码之AQS中断设计与实现(内涵jvm部分实现)

作者: starskye | 来源:发表于2021-03-31 17:23 被阅读0次

    本章内容为AQS对线程中断做的处理进行讲解.再之前文章中有讲述AQS的队列原理使用的是park和unpark,既然如此那么思考下几个问题?

    1. 队列中或者获取锁时是否支持中断?
    2. 如果是那么他的中断做了什么及如何设计的?
    //针对第一个问题,AQS是支持中断的他提供了四个方法,可以查看分为两类
    //1.不抛出中断异常 lock/tryLock
    //2.抛出中断异常 lockInterruptibly/tryLock(long time, TimeUnit unit)
    void lock();
    boolean tryLock();
    
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    //如果但看这四个方法会以为第二种支持,第一种不支持,真的是这样吗?下面详细讲解其原理.
    //jvm中断实现
    void os::interrupt(Thread* thread) {
      assert(Thread::current() == thread || Threads_lock->owned_by_self(),
        "possibility of dangling Thread pointer");
    
      OSThread* osthread = thread->osthread();
    
      if (!osthread->interrupted()) {
        osthread->set_interrupted(true);
        // More than one thread can get here with the same value of osthread,
        // resulting in multiple notifications.  We do, however, want the store
        // to interrupted() to be visible to other threads before we execute unpark().
        OrderAccess::fence();
        ParkEvent * const slp = thread->_SleepEvent ;
        if (slp != NULL) slp->unpark() ;
      }
    
      // For JSR166. Unpark even if interrupt status already was set
      if (thread->is_Java_thread())
        ((JavaThread*)thread)->parker()->unpark();
      //如果产生中断则会调用线程的unpark方法,而此时线程正处于park挂起状态,其原理查看前面两章
      ParkEvent * ev = thread->_ParkEvent ;
      if (ev != NULL) ev->unpark() ;
    }
    //我们发现发生中断会使得接触park挂起继续执行
    //而park中断后紧接着返回当前的中断事件并且清除中断状态
    //清除并且记录了当前的状态方便后续使用,下面查看此方法的调用出
    private final boolean parkAndCheckInterrupt() {
            LockSupport.park(this);
            return Thread.interrupted();
    }
    //此次方法为lockInterruptibly方法内部使用,可以查看当park被唤醒后如果是中断状态则直接抛出异常
    private void doAcquireInterruptibly(int arg){
    ....
      if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                        throw new InterruptedException();
    ...
    }
    //此为tryLock(long time, TimeUnit unit)方法的调用,虽然此处没有使用parkAndCheckInterrupt 但是其原理相同
    //并且判断是否为中断,如果存在中断则抛出中断异常
    private boolean doAcquireNanos(int arg, long nanosTimeout)
    ...
          if (shouldParkAfterFailedAcquire(p, node) &&
                        nanosTimeout > spinForTimeoutThreshold)
                        LockSupport.parkNanos(this, nanosTimeout);
                    if (Thread.interrupted())
                        throw new InterruptedException();
    ...
    }
    //到此已经知道了申明中断的地方,下面为未申明的第一类获取锁的方法
    //lock方法的调用,可以看出此处将之前抛出异常改为了中断状态interrupted,而此方法的返回值便是中断状态
    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)) {
                        setHead(node);
                        p.next = null; // help GC
                        failed = false;
                        return interrupted;
                    }
                    if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                        //需要提醒的是parkAndCheckInterrupt 清除了中断,并且此处可以看出中断并不影响获取锁
                        //因为此处理在for(;;)中,将会继续尝试获取锁,直到获取锁成功然后返回interrupted 
                        interrupted = true;
                }
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
    }
    //tryLock,只是一个非公平锁获取并不涉及到中断状态,其结果为返回是否获取锁成功 
    final boolean nonfairTryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                    if (compareAndSetState(0, acquires)) {
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                else if (current == getExclusiveOwnerThread()) {
                    int nextc = c + acquires;
                    if (nextc < 0) // overflow
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
                return false;
    }
    

    到此处两类的四种方法实现都已经讲解完成,下面做总结.

    1. AQS支持中断,并且做了两种处理.
      1. 抛出中断异常
      1. 记录中断状态并且返回,在记录的同时清除线程的中断状态
    1. 不抛出异常的中断处理,也分为两种
      1. tryLock 直接获取锁,打破公平锁(此处需要注意)
      1. lock 获取锁直到成功.忽略所有中断,并且记录中断状态,最终返回到acquire方法,此方法如果发现当前线程被中断,则处理调用线程的interrupt方法,将当前线程再次设置上中断状态(还记得之前提的记录会清除,而此处再次还原),说明lock的中断需要我们进行处理,否则将会在后续的一些操作中如sleep等可中断方法中抛出异常
    //此为sleep的实现,如果采用lock 被中断后执行到sleep方法此处将会判断中断状态,如果为true则抛出异常
    //并且清除中断状态
      if (Thread::is_interrupted (THREAD, true) && !HAS_PENDING_EXCEPTION) {
        THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
      }
    

    相关文章

      网友评论

          本文标题:看!源码之AQS中断设计与实现(内涵jvm部分实现)

          本文链接:https://www.haomeiwen.com/subject/cmqahltx.html