interrupt,interrupted和isInterrupted以及AQS中的中断方法

1、interrupt,interrupted和isInterrupted的区别
1.1 interrupt

interrupt()是真正触发中断的方法。

public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }
  1. sleep、wait、join等方法都统一的对外抛出InterruptedException,所以在调用interrupt方法时是会被唤醒的,然后就会抛出InterruptedException,线程虽然被中断,但此时线程状态状态会被重置为false,下面举例。
  2. 非nio、synchronized和ReentrantLock.lock(),如果线程处于这些阻塞状态,只能等待io处理完成或者等待到锁之后,程序才能向下执行,从而通过判断中断变量来结束。nio和ReentrantLock.tryLock(long timeout, TimeUnit unit) throws InterruptedException,如果线程处于这些阻塞状态,是可以被唤醒的。你也可以调用reentrantLock.lockInterruptibly() throws InterruptedException方法,它就相当于一个超时设为无限的tryLock方法。其实从方法声明上我们也可以判断出来,因为对外抛出了InterruptedException。
  3. LockSupport.park()也会被中断,但是不会抛出异常。下面也会举例。
1.2 interrupted和isInterrupted

对于每一个线程,都会有一个Boolean类型中断标志位,该标志位默认false,表示没有被中断.

  1. isInterrupted() 方法来查看当前的中断标志位的状态,该标志位不影响线程的状态。
  2. interrupted() 也是可以检查当前线程是否中断,但在调用该方法后,如果标志位为true,该方法会将标志位重置为false。

举例:

     public static void test1(){
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                //被中断后,抛出异常后,中断标志会被置为false
                System.out.println(Thread.currentThread().isInterrupted());
                //此时再调用 中断方法,交给线程自己处理
                Thread.currentThread().interrupt();
            }
            //此时中断状态就会变为true,而且调用也不会变更中断标志
            System.out.println(Thread.currentThread().isInterrupted());
            System.out.println(Thread.currentThread().isInterrupted());
            //标志位为true,该方法会将标志位重置为false
            System.out.println(Thread.interrupted());
            //所以这个会返回false,包括下面的方法也会返回false
            System.out.println(Thread.interrupted());
            System.out.println(Thread.currentThread().isInterrupted());
        });
        thread.start();
        thread.interrupt();
    }

结果

false
true
true
true
false
false
2、LockSupport
  1. Thread对象的native实现里有一个成员代表线程的中断状态,我们可以认为它是一个bool型的变量。初始为false。

  2. Thread对象的native实现里有一个成员代表线程是否可以阻塞的许可permit,我们可以认为它是一个int型的变量,但它的值只能为0或1。当为1时,再累加也会维持1。初始为0。

LockSupport 中的park和unpark会对这个permit进行处理。

下面将以伪代码的实现来说明park/unpark的实现。

park() {
    if(permit > 0) {
        permit = 0;
        return;
    }

    if(中断状态 == true) {
        return;
    }

    阻塞当前线程;  // 将来会从这里被唤醒

    if(permit > 0) {
        permit = 0;
    }
}

可见,只要permit为1或者中断状态为true,那么执行park就不能够阻塞线程。park只可能消耗掉permit,但不会去消耗掉中断状态。

unpark(Thread thread) {
    if(permit < 1) {
        permit = 1;
        if(thread处于阻塞状态)
            唤醒线程thread;
    }
}

unpark一定会将permit置为1,如果线程阻塞,再将其唤醒。从实现可见,无论调用几次unpark,permit只能为1。

  1. park调用后一定会消耗掉permit,无论unpark操作先做还是后做。
  2. 如果中断状态为true,那么park无法阻塞。
  3. unpark会使得permit为1,并唤醒处于阻塞的线程。
  4. interrupt()会使得中断状态为true,并调用unpark。
  5. sleep() / wait() / join()调用后一定会消耗掉中断状态,无论interrupt()操作先做还是后做。

举例:
1、unpark 不会修改中断状态

    public static void test1(){
        Thread thread = new Thread(() -> {
            LockSupport.park();
            System.out.println(Thread.currentThread().isInterrupted());
            System.out.println(Thread.currentThread().isInterrupted());
            System.out.println(Thread.interrupted());
            System.out.println(Thread.interrupted());
            System.out.println(Thread.currentThread().isInterrupted());
        });
        thread.start();
        //unpark 不会修改中断状态
        LockSupport.unpark(thread);
    }

false
false
false
false
false

2、interrupt() 可以中断park()

    public static void test1(){
        Thread thread = new Thread(() -> {
            LockSupport.park();
            //此时中断状态就会变为true,而且调用也不会变更中断标志
            System.out.println(Thread.currentThread().isInterrupted());
            System.out.println(Thread.currentThread().isInterrupted());
            //标志位为true,该方法会将标志位重置为false
            System.out.println(Thread.interrupted());
            //所以这个会返回false,包括下面的方法也会返回false
            System.out.println(Thread.interrupted());
            System.out.println(Thread.currentThread().isInterrupted());
        });
        thread.start();
        thread.interrupt();
    }
true
true
true
false
false
AQS中的parkAndCheckInterrupt()

我们熟悉的可以知道 AQS在入队之后,如果没有获取到锁,会调用parkAndCheckInterrupt(),这里就会阻塞线程,等待被唤醒。

 private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.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())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
   public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    
  static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }

但是Thread.interrupted()的意义是什么?首先当前线程等着被释放锁调用LockSupport.unpark()或者被其他线程中断,LockSupport.unpark() 唤醒是不会改变当前线程状态的,所以此方法会返回false,回到acquireQueued().死循环继续获取锁。
但是如果被其他线程调用interrupt(),此方法会返回true,但此线程的中断状态也会被变更为false。之所以要变为false,是因为,如果再循环获取锁获取不到时,会再次调用 LockSupport.park(this);此时中断状态为true,是无法阻塞程序的。所以要重置断状态。此时再返回外层循环acquireQueued方法,中断的标志会被 设置 interrupted = true;

selfInterrupt(); 猜测是为了给当前线程设置一个被中断过的标志,由外层线程自己处理。

参考文章1
参考文章2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值