美文网首页
看!源码之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