zoukankan      html  css  js  c++  java
  • Java多线程17:中断机制

    概述

    之前讲解Thread类中方法的时候,interrupt()、interrupted()、isInterrupted()三个方法没有讲得很清楚,只是提了一下。现在把这三个方法同一放到这里来讲,因为这三个方法都涉及到多线程的一个知识点----中断机制。

    Java没有提供一种安全、直接的方法来停止某个线程,而是提供了中断机制。中断机制是一种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理。有个例子举个蛮好,就像父母叮嘱出门在外的子女要注意身体一样,父母说了,但是子女是否注意身体、如何注意身体,还是要看自己。

    中断机制也是一样的,每个线程对象里都有一个标识位表示是否有中断请求(当然JDK的源码是看不到这个标识位的,是虚拟机线程实现层面的),代表着是否有中断请求。

    三个中断方法

    上面说了,中断标识位是JDK源码看不到的,是虚拟机线程实现层面的。下面结合代码逐一看一下这三个方法的作用,以及为什么中断标识位是虚拟机实现层面的:

    1、interrupt()

     1 public void interrupt() {
     2     if (this != Thread.currentThread())
     3         checkAccess();
     4 
     5     synchronized (blockerLock) {
     6         Interruptible b = blocker;
     7         if (b != null) {
     8         interrupt0();        // Just to set the interrupt flag
     9         b.interrupt();
    10         return;
    11         }
    12     }
    13     interrupt0();
    14     }
    1 /* Some private helper methods */
    2 private native void setPriority0(int newPriority);
    3 private native void stop0(Object o);
    4 private native void suspend0();
    5 private native void resume0();
    6 private native void interrupt0();

    分两部分看:

    (1)第一部分的第8行注释说得很清楚了,interrupt0()方法的作用是"Just to set the interrupt flag",即方法的作用仅仅是设置中断标识位

    (2)第二部分的第6行就是interrupt0()方法的原型,由于方法是被native修饰的,很明显这是一个本地方法,是Java虚拟机实现的

    2、isInterrupted()

    方法唯一的作用只是测试线程是否已经中断,中断标识位的状态并不受到该方法的影响,看一下Java是如何实现这个方法的:

     1 /**
     2  * Tests whether this thread has been interrupted.  The <i>interrupted
     3  * status</i> of the thread is unaffected by this method.
     4  *
     5  * <p>A thread interruption ignored because a thread was not alive 
     6  * at the time of the interrupt will be reflected by this method 
     7  * returning false.
     8  *
     9  * @return  <code>true</code> if this thread has been interrupted;
    10  *          <code>false</code> otherwise.
    11  * @see     #interrupted()
    12  * @revised 6.0
    13  */
    14 public boolean isInterrupted() {
    15 return isInterrupted(false);
    16 }
    private native boolean isInterrupted(boolean ClearInterrupted);

    注意一下第一部分的第2行和第3行,"The interrupted statis of the thread is unaffected by this method",即线程的中断状态不受到这个方法的影响。最终调用的是isInterrupted(boolean ClearInterrupted),这个方法是一个native的,看得出也是Java虚拟机实现的。方法的参数ClearInterrupted,顾名思义,清除中断标识位,这里传递false,明显就是不清除

    3、interrupted()

    方法的作用是测试当前线程是否已经中断,线程的中断标识位由该方法清除。换句话说,连续两次调用该方法的返回值必定是false。看一下这个方法是如何实现的:

     1 /**
     2  * Tests whether the current thread has been interrupted.  The
     3  * <i>interrupted status</i> of the thread is cleared by this method.  In
     4  * other words, if this method were to be called twice in succession, the
     5  * second call would return false (unless the current thread were
     6  * interrupted again, after the first call had cleared its interrupted
     7  * status and before the second call had examined it).
     8  *
     9  * <p>A thread interruption ignored because a thread was not alive 
    10  * at the time of the interrupt will be reflected by this method 
    11  * returning false.
    12  *
    13  * @return  <code>true</code> if the current thread has been interrupted;
    14  *          <code>false</code> otherwise.
    15  * @see #isInterrupted()
    16  * @revised 6.0
    17  */
    18 public static boolean interrupted() {
    19 return currentThread().isInterrupted(true);
    20 
    private native boolean isInterrupted(boolean ClearInterrupted);

    同样,第2行和第3行的注释已经写得很清楚了,"The interrupted status of the thread is cleared by this method",即线程的中断状态由此方法清除。另外,interrupted()方法和isInterrupted()方法调用的是同一个native方法,无非这个方法传入的是true,表示清除中断标识位

    此外,JDK API中有些类的方法也可能会调用中断,比如FutureTask的cancel,如果传入true则会在正在运行的异步任务上调用interrupt()方法,又如ThreadPoolExecutor中的shutdownNow方法会遍历线程池中的工作线程并调用线程的interrupt()方法。这些场景下只要代码没有对中断作出响应,那么任务将一直执行下去。

    中断处理时机

    这其实是一个很宽泛的、没有标注答案的话题。显然,作为一种协作机制,不会强求被中断的线程一定要在某个点进行中断处理。实际上,被中断线程只需要在合适的时候处理即可,如果没有合适的时间点,甚至可以不处理。"合适的时间点"就和业务逻辑密切相关了。

    处理时机决定着程序的效率和响应的灵敏度。频繁的检查中断可能会导致程序执行效率低下,较少的检查则可能导致中断请求得不到及时响应。在实际场景中,如果性能指标比较关键,可能需要建立一个测试模型来分析最佳的中断检测点,以平衡性能和响应灵敏性

    线程中断举例

    写了这么多理论,写一个例子来演示一下中断:

    public static void main(String[] args) throws Exception
    {
        Runnable runnable = new Runnable()
        {
            public void run()
            {
                while (true)
                {
                    if (Thread.currentThread().isInterrupted())
                    {
                        System.out.println("线程被中断了");
                        return ;
                    }
                    else
                    {
                        System.out.println("线程没有被中断");
                    }
                }
            }
        };
        Thread t = new Thread(runnable);
        t.start();
        Thread.sleep(3000);
        t.interrupt();
        System.out.println("线程中断了,程序到这里了");
    }

    看一下运行结果:

    ...
    线程没有被中断
    线程没有被中断
    线程没有被中断
    线程没有被中断
    线程没有被中断
    线程中断了,程序到这里了
    线程被中断了

    代码分为以下几步:

    1、main函数起一个t线程

    2、main函数3秒钟之后给t线程打一个中断标识位,表示t线程要中断

    3、t线程无限轮询自己的中断标识位,中断了则打印、退出,否则一直运行

    从控制台上打印的语句看到,3秒钟中断后,打印出该打印的语句后,就停止了。那这种场景就是前面说的"频繁地检查",导致程序效率低下;那如果不频繁地检查呢,比如在while中的else分支中加上Thread.sleep(500),表示500ms即0.5s检查一次,那这种场景就是前面说的"中断得不到及时的响应"。

    其实这个例子中,t线程完全可以不用去管这个中断标识位的,不去检查就好了,只管做自己的事情,这说明中断标识位设不设置是别人的事情,处不处理是我自己的事情,没有强制要求必须处理中断。

    但是,那些会抛出InterruptedException的方法要除外。像sleep、wait、notify、join,这些方法遇到中断必须有对应的措施,可以直接在catch块中处理,也可以抛给上一层。这些方法之所以会抛出InterruptedException就是由于Java虚拟机在实现这些方法的时候,本身就有某种机制在判断中断标识位,如果中断了,就抛出一个InterruptedException。

  • 相关阅读:
    Python 模块 itertools
    Python 字符串的encode与decode
    python 模块 hashlib(提供多个不同的加密算法)
    暴力尝试安卓gesture.key
    hdu 1300 Pearls(DP)
    hdu 1232 畅通工程(并查集)
    hdu 1856 More is better(并查集)
    hdu 1198 Farm Irrigation(并查集)
    hdu 3635 Dragon Balls(并查集)
    hdu 3038 How Many Answers Are Wrong(并查集)
  • 原文地址:https://www.cnblogs.com/xrq730/p/4856361.html
Copyright © 2011-2022 走看看