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

    前言:在Java多线程中,中断一直围绕着我们,当我们阅读各种关于Java多线程的资料、书籍时,“中断”一词总是会出现,笔者对其的理解也是朦朦胧胧,因此非常有必要搞清楚Java多线程的中断机制。

    1.Java中断机制是什么

    Java 中断机制是一种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理中断。这好比老师要求学生要高质量完成作业,但是学生是否高质量完成作业,完全取决于学生自己。

    Java 中断模型非常的简单:每个线程对象里都有一个 boolean 类型的标识(当然jdk源码中是看不到该标识位,它位于虚拟机层面),代表着是否有中断请求(该请求可以来自所有线程,包括被中断的线程本身)。例如,当线程 t1 想中断线程 t2,只需要在线程 t1 中将线程 t2 对象的中断标识置为 true,然后线程 t2 可以选择在合适的时候处理该中断请求,甚至可以不理会该请求,就像这个线程没有被中断一样。

    综合上述两段文字的描述,对Java中断机制进行总结:Java中断是一种机制,并不是真的停止线程,而是对线程对象打上一个中断标记,具体如何处理还是要看被中断线程如何操作。

    2.Thread类提供的中断相关方法

    中断线程,注意该方法未被static修饰,因此该方法被Thread对象调用。并且该方法仅仅是为线程打一个中断的标记,将线程中断状态设置为true。

    测试Thread对象是否中断,主要该方法也未被static修饰,因此该方法也应该被Thread对象调用,如果线程被打了中断标记,返回true,否则返回false。特别注意该方法不影响中断状态,这里主要和interrupted()方法做对比。

    测试当前线程是否中断,注意该方法被static修饰,并且该方法会清除线程的中断标记,将中断标记设置为false。也就是说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。

    总结:

    #1.真正中断线程的方法为interrupt(),并且该方法也仅仅是为Thread对象打一个中断的标记,而不是立即终止线程。

    #2.isInterrupted()方法未被static修饰,测试Thread对象是否中断,也就是判断线程对象是否有中断标记。该方法不会清除中断标记

    #3.interrupted()方法被static修饰,测试当前线程是否中断,注意该方法会清除线程中断的标记

    以上三个函数的源码逻辑简单,主要调用了native方法,这里不进行阐述。

    3.中断处理时机

    中断作为一种协作机制,不会强求被中断线程一定要在某个点进行处理。实际上,被中断线程只需在合适的时候处理即可,如果没有合适的时间点,甚至可以不处理,这时候在任务处理层面,就跟没有调用中断方法一样。“合适的时候”与线程正在处理的业务逻辑紧密相关。

    处理时机决定着程序的效率与中断响应的灵敏性,频繁的检查中断状态可能会使程序执行效率下降,相反,检查的较少可能使中断请求得不到及时响应。如果发出中断请求之后,被中断的线程继续执行一段时间不会给系统带来灾难,那么就可以将中断处理放到方便检查中断,同时又能从一定程度上保证响应灵敏度的地方。当程序的性能指标比较关键时,可能需要建立一个测试模型来分析最佳的中断检测点,以平衡性能响应灵敏性

    4.线程中断举例

    • 停不下来的线程
     1 public static void main(String[] args) throws InterruptedException {
     2         Thread t1 = new Thread(() -> {
     3             for (int i = 0; i < 500000; i++) {
     4                 System.out.println("i=" + (i + 1));
     5             }
     6             System.out.println("我是t1线程");
     7         });
     8         t1.start();
     9         Thread.sleep(200);
    10         t1.interrupt();
    11     }

    上述代码运行结果如下:

    从运行结果来看,线程并未终止成功,这也符合interrupt()函数的功能描述,仅仅是为线程打一个中断标记,具体怎么处理还要看线程自己如何操作。

    将上面代码做如下修改:

     1 public static void main(String[] args) throws InterruptedException {
     2         Thread t1 = new Thread(() -> {
     3             for (int i = 0; i < 500000; i++) {
     4                 if (Thread.currentThread().isInterrupted()) {  // 对中断做处理
     5                     System.out.println("t1线程被中断了");
     6                     return;
     7                 }
     8                 System.out.println("i=" + (i + 1));
     9             }
    10             System.out.println("我是t1线程");
    11         });
    12         t1.start();
    13         Thread.sleep(200);
    14         t1.interrupt();
    15     }

    其运行结果如下:

    上述代码对中断进行了处理,所以循环并未走完,t1线程被成功中断。

    • interrupted()和isInterrupted()的区别
     1 public static void main(String[] args) throws InterruptedException {
     2         Thread t1 = new Thread(() -> {
     3             for (int i = 0; i < 500000; i++) {
     4                 System.out.println("i=" + (i + 1));
     5             }
     6             System.out.println("我是t1线程");
     7         });
     8         t1.start();
     9         Thread.sleep(200);
    10         t1.interrupt();
    11         System.out.println("isInterrupted()=" + t1.isInterrupted());
    12         System.out.println("isInterrupted()=" + t1.isInterrupted());
    13         System.out.println("interrupted()=" + t1.interrupted());
    14         System.out.println("interrupted()=" + Thread.interrupted());
    15     }

    运行结果如下:

    为什么会出现上面的运行结果呢,从源码上最容易理解:

    • 该方法未被static修饰【isInterrupted(false)表示不会清除中断标志,isInterrupted为native方法】,所以该方法被Thread对象调用,返回Thread对象的中断状态。
    • 该方法被static修饰【注意isInterrupted(true)表示会清除中断标志】,该方法返回当前线程的中断状态,在上述代码中,当前线程为main方法代表的主线程,并没有进行中断操作,所以打印结果为false。

    修改上述代码:

     1 public static void main(String[] args) throws InterruptedException {
     2         Thread t1 = new Thread(() -> {
     3             for (int i = 0; i < 500000; i++) {
     4                 System.out.println("i=" + (i + 1));
     5             }
     6             System.out.println("我是t1线程");
     7         });
     8         t1.start();
     9         Thread.sleep(200);
    10         t1.interrupt();
    11         Thread.currentThread().interrupt();
    12         System.out.println("isInterrupted()=" + t1.isInterrupted());
    13         System.out.println("isInterrupted()=" + t1.isInterrupted());
    14         System.out.println("interrupted()=" + t1.interrupted());
    15         System.out.println("interrupted()=" + Thread.interrupted());
    16     }

    注意第11行代码,其运行结果如下:

    从结果充分说明连续两次调用interrupted()会清除中断标记。

    总结

    通过上述的分析,对Java的中断机制的核心要点做如下总结:

    • Java中断机制是一种协作机制,中断只是给线程打一个中断标记,具体如何操作还要看线程自己,by myself。
    • interrupt()函数作用仅仅是为线程打一个中断标记
    • interrupted()与isInterrupted()函数,都是返回线程的中断状态,但是interrupted()被static修饰,返回当前线程的中断状态,并且会清除线程的中断标记;而isInterrupted()未被static修饰,被Thread对象调用,它不会清除线程的中断标记

    by Shawn Chen,2019.02.17,上午。 

  • 相关阅读:
    常用学习网站 (转载)
    Cookie 与 Session 的区别(转载)
    Asp.net 控件ClientID,UniqueID命名规则问题
    HTTP POST GET 本质区别详解
    asp net中的状态管理的方式有哪几种分别有哪些优势和缺点
    http 协议 (转载)
    Java 学习 (转载)
    数据库日常维护常用的脚本部分收录(转载)
    ASP.NET页面与IIS底层交互和工作原理详解(转载)
    Web网站的性能测试工具(转载)
  • 原文地址:https://www.cnblogs.com/developer_chan/p/10388282.html
Copyright © 2011-2022 走看看