美文网首页
interrupt()中断行为研究

interrupt()中断行为研究

作者: 王侦 | 来源:发表于2019-07-27 14:21 被阅读0次

1.中断后LockSupport.park()直接返回

public class MultInterruptParkDemo {
    public static volatile boolean flag = true;
    public static void main(String[] args) {
        ThreadDemo04 t4 = new ThreadDemo04();
        t4.start();
        t4.interrupt();
        flag = false;
    }
    public static class ThreadDemo04 extends Thread {
        @Override
        public void run() {
            while (flag) {
            }
            LockSupport.park();
            System.out.println("本打印出现在第一个park()之后");
            LockSupport.park();
            System.out.println("本打印出现在第二个park()之后");
        }
    }
}

结果:

本打印出现在第一个park()之后
本打印出现在第二个park()之后

1.1 分析

Thread中断方法分析(包括HotSpot源码)中interrupt源码:

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);

如果线程未设置中断标志位,会进行设置。

LockSupport及HotSpot层Parker::park/unpark分析中park源码:

void Parker::park(bool isAbsolute, jlong time) {
  // Ideally we'd do something useful while spinning, such
  // as calling unpackTime().

  // Optional fast-path check:
  // Return immediately if a permit is available.
  // We depend on Atomic::xchg() having full barrier semantics
  // since we are doing a lock-free update to _counter.
  if (Atomic::xchg(0, &_counter) > 0) return;

  Thread* thread = Thread::current();
  assert(thread->is_Java_thread(), "Must be JavaThread");
  JavaThread *jt = (JavaThread *)thread;

  // Optional optimization -- avoid state transitions if there's an interrupt pending.
  // Check interrupt before trying to wait
  if (Thread::is_interrupted(thread, false)) {
    return;
  }

只要线程设置了中断标志位,就直接返回,且这里的Thread::is_interrupted(thread, false)因为是false,所以不会清除中断标志位。

1.2 总结

  • t4.interrupt()会设置线程的中断标志位
  • LockSupport.park()会检查线程是否设置了中断标志位,如果设置了,则返回(这里并不会清除中断标志位)

2.unpark不会累积许可(最多为1)

public class MultInterruptParkDemo2 {
    public static volatile boolean flag = true;
    public static void main(String[] args) {
        ThreadDemo04 t4 = new ThreadDemo04();
        t4.start();
        LockSupport.unpark(t4);
        LockSupport.unpark(t4);
        LockSupport.unpark(t4);
        flag = false;
    }
    public static class ThreadDemo04 extends Thread {
        @Override
        public void run() {
            while (flag) {
            }
            LockSupport.park();
            System.out.println("本打印出现在第一个park()之后");
            LockSupport.park();
            System.out.println("本打印出现在第二个park()之后");
        }
    }
}

结果:

本打印出现在第一个park()之后

2.1 分析

LockSupport及HotSpot层Parker::park/unpark分析中park源码:

void Parker::park(bool isAbsolute, jlong time) {
  // Ideally we'd do something useful while spinning, such
  // as calling unpackTime().

  // Optional fast-path check:
  // Return immediately if a permit is available.
  // We depend on Atomic::xchg() having full barrier semantics
  // since we are doing a lock-free update to _counter.
  if (Atomic::xchg(0, &_counter) > 0) return;

每次调用park都会将_counter直接置为0。

void Parker::unpark() {
  int s, status ;
  status = pthread_mutex_lock(_mutex);
  assert (status == 0, "invariant") ;
  s = _counter;
  _counter = 1;

每次调用unpark都会将_counter直接置为1.

2.2 总结

  • 因此,总的许可数总是保持在1,无论调用多少次unpark,都只会将_counter置为1。
  • 每次park都会将_counter置为0,如果之前为1,则直接返回。后面的park调用就会阻塞。

3.Thread.interrupted()会清除中断标志

public class MultInterruptParkDemo3 {
    public static volatile boolean flag = true;
    public static void main(String[] args) {
        ThreadDemo04 t4 = new ThreadDemo04();
        t4.start();
        t4.interrupt();
        flag = false;
    }
    public static class ThreadDemo04 extends Thread {
        @Override
        public void run() {
            while (flag) {
            }
            LockSupport.park();
            System.out.println("本打印出现在第一个park()之后");
            System.out.println(Thread.interrupted());
            System.out.println(Thread.interrupted());
            LockSupport.park();
            System.out.println("本打印出现在第二个park()之后");
        }
    }
}

结果:

本打印出现在第一个park()之后
true
false

3.1 分析

Thread中断方法分析(包括HotSpot源码)中Thread.interrupted()源码:

    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }
bool os::is_interrupted(Thread* thread, bool clear_interrupted) {
  assert(Thread::current() == thread || Threads_lock->owned_by_self(),
    "possibility of dangling Thread pointer");

  OSThread* osthread = thread->osthread();

  bool interrupted = osthread->interrupted();

  if (interrupted && clear_interrupted) {
    osthread->set_interrupted(false);
    // consider thread->_SleepEvent->reset() ... optional optimization
  }

  return interrupted;
}

可以看到如果中断了并且clear_interrupted为true,则会清除中断标志位。

3.2 总结

Thread.interrupted()清除中断标志位后,第二个LockSupport.park()就不会直接返回,而是正常地阻塞。

4.sleep清除中断标志位但不会更改_counter

public class MultInterruptParkDemo4 {
    public static volatile boolean flag = true;
    public static void main(String[] args) {
        ThreadDemo04 t4 = new ThreadDemo04();
        t4.start();
        t4.interrupt();
        flag = false;
    }
    public static class ThreadDemo04 extends Thread {
        @Override
        public void run() {
            while (flag) {
            }
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("本打印出现在第一个sleep()之后");
            System.out.println(Thread.interrupted());
            System.out.println(Thread.interrupted());
            LockSupport.park();
            System.out.println("本打印出现在第二个park()之后");
        }
    }
}

结果

java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at wz.interrupt.MultInterruptParkDemo4$ThreadDemo04.run(MultInterruptParkDemo4.java:19)
本打印出现在第一个sleep()之后
false
false
本打印出现在第二个park()之后

4.1 分析

Thread中断方法分析(包括HotSpot源码)中interrupt源码:

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();

  ParkEvent * ev = thread->_ParkEvent ;
  if (ev != NULL) ev->unpark() ;

}

会调用Parker::unpark(),那么_counter会设置为1.

参考阻塞线程的相关方法分析(包括HotSpot层源码)中关于sleep源码:

JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
  JVMWrapper("JVM_Sleep");

  if (millis < 0) {
    THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
  }

  if (Thread::is_interrupted (THREAD, true) && !HAS_PENDING_EXCEPTION) {
    THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
  }

如果线程已中断,则清除中断标记并抛出中断异常,直接返回,但是并没有更改_counter。

4.2 总结

  • interrupt会调用((JavaThread*)thread)->parker()->unpark(),将_counter设置为1,也即后面调用park不会阻塞
  • sleep如果检测到中断会直接清除中断标志,并抛出异常。
  • 因此两个Thread.interrupted()都返回false,且LockSupport.park()不会阻塞。

5.对于LockSupport.park()阻塞总结

如下两个条件任何一个成立,park()都不会阻塞:

  • 中断标志位存在(wait、join、sleep或Thread.interrupted()都会清除中断标志位)
  • _counter为1(之前调用了unpark或者interrupt)

参考

相关文章

  • interrupt()中断行为研究

    1.中断后LockSupport.park()直接返回 结果: 1.1 分析 Thread中断方法分析(包括Hot...

  • StampedLock错误用法:非中断锁线程调用interrup

    1.错误用法:在调用readLock()线程中进行中断 根据interrupt()中断行为研究可知,由于线程设置了...

  • 中断 2022-01-27

    中断(interrupt) from wiki:中断(Interrupt)是指处理器[https://zh.wik...

  • 中断Interrupt

    参考: https://www.cnblogs.com/lujiango/p/7641983.html[https...

  • 线程相关(六)interrput 和 yield

    interrupt调用interrupt(),通知线程应该中断了。这个很有意思,是通知线程应该中断,而不是中断线程...

  • Java线程Thread之interrupt中断解析

    这一篇我们说说Java线程Thread的interrupt中断机制。 interrupt之中断状态标记 inter...

  • Thread

    Thread-中断 interrupt() interrupt()用于中断线程,调用该方法的线程的状态将被置为"中...

  • 多线程基础

    多线程基础总结 一、线程中断 interrupt方法用来请求终止线程。 1. interrupt置位中断标志位 当...

  • interrupt、interrupted和isInterrup

    1.interrupt() interrupt方法用于中断线程。调用该方法的线程的状态为将被置为"中断"状态。 注...

  • java线程相关

    中断一个正在运行的线程 中断阻塞线程的方法 我们调用线程对象的interrupt()方法时会产生Interrupt...

网友评论

      本文标题:interrupt()中断行为研究

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