java多线程的interrupt

近期学习Java多线程的中断机制,网上的帖子说得很浅,并没深究其原理。看了Java源码,对Java的中断机制有了略深入的理解,在这篇文章中向感兴趣的网友分享下。这篇文章主要通过一个典型例子对中断机制进行剖析。

  一:一些概念和重要方法

  interrupt status(中断状态):请记住这个术语,中断机制就是围绕着这个字段来工作的。在Java源码中代表中断状态的字段是:private volatile Interruptible blocker;对“Interruptible”这个类不需要深入分析,对于“blocker”变量有以下几个操作。

  1.默认blocker=null; ®1

  2.调用方法“interrupt0();”将会导致“该线程的中断状态将被设置(JDK文档中术语)”。®2

  3.再次调用“interrupt0();”将会导致“其中断状态将被清除(同JDK文档中术语)”®3

  注:这三点很重要,接下来文章中会用来®1®2®3代替。

明白了第一点来看下文档中对于中断线程相关方法的描述。

  1.public void interrupt();

    中断线程。如果线程在调用 Object 类的 wait()wait(long) wait(long, int) 方法,或者该类的 join()join(long)join(long, int)sleep(long) sleep(long, int) 方法

  过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。 

  2.public static boolean interrupted();

    测试当前线程是否已经中断。线程的中断状态 由该方法清除。线程中断被忽略,因为在中断时不处于活动状态的线程将由此返回 false 的方法反映出来。

      返回:
    如果当前线程已经中断,则返回 true;否则返回 false
      另请参见:
isInterrupted()

  3.public boolean isInterrupted();

    测试线程是否已经中断。线程的中断状态 不受该方法的影响。线程中断被忽略,因为在中断时不处于活动状态的线程将由此返回 false 的方法反映出来。

  返回:如果该线程已经中断,则返回 true;否则返回 false

  另请参见:interrupted()

  <!--来自JDK API文档-->

    以上三段中关于线程的中断状态 由该方法清除的描述,在源码层面就是®3调用。

 接下来说一下"interrupted()"和"isInterrupted()"两个方法的相同点和不同点。在这之前看一下源码中两个方法的代码,如下:

复制代码
 1 public static boolean interrupted() {
 2     return currentThread().isInterrupted(true);
 3     }
 4 public boolean isInterrupted() {
 5     return isInterrupted(false);
 6     }
 7  /**
 8      * Tests if some Thread has been interrupted.  The interrupted state
 9      * is reset or not based on the value of ClearInterrupted that is
10      * passed.
11      */
12 private native boolean isInterrupted(boolean ClearInterrupted);
复制代码

  相同点都是判断线程的interrupt status是否被设置,若被设置返回true,否则返回false.区别有两点:一:前者是static方法,调用者是current thread,而后者是普通方法,调用者是this current.二:它们其实都调用了Java中的一个native方法isInterrupted(boolean ClearInterrupted); 不同的是前者传入了参数true,后者传入了false.意义就是:前者将清除线程的interrupt state(®3),调用后者线程的interrupt state不受影响。

二:例子。

  接下来看一个例子,这个例子说明了两个问题。1.调用interrupt()方法并不会中断一个正在运行的线程.2.若调用sleep()而使线程处于阻塞状态,这时调用interrupt()方法,会抛出InterruptedException,从而使线程提前结束阻塞状态,退出阻塞代码。如下:

复制代码
 1 package interrupt;
 2 
 3 public class Main {
 4     /**
 5      * @param args
 6      */
 7     public static void main(String[] args) {
 8         Main main = new Main();
 9         Thread t = new Thread(main.runnable);
10         System.out.println("mainmainmain");
11         t.start();
12         try {
13             Thread.sleep(2000);
14         } catch (InterruptedException e) {
15             // TODO Auto-generated catch block
16             e.printStackTrace();
17         }
18         t.interrupt();
19     }
20 
21     Runnable runnable = new Runnable() {
22         @Override
23         public void run() {
24             int i = 0;
25             try {
26                 while (i < 1000) {
27                     Thread.sleep(500);
28                     System.out.println(i++);
29                 }
30             } catch (InterruptedException e) {
31                 e.printStackTrace();
32             }
33         }
34     };
35 }
复制代码

运行结果:

复制代码
1 mainmainmain
2 0
3 1
4 2
5 java.lang.InterruptedException: sleep interrupted
6     at java.lang.Thread.sleep(Native Method)
7     at interrupt.Main$1.run(Main.java:27)
8     at java.lang.Thread.run(Thread.java:619)
复制代码

三:分析下例子中的中断机制

 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     }
复制代码

如上是Java源码中的代码,由此我们看出问题的答案。线程的blocker字段(也就是interrupt status)默认是null(®1)。调用interrupt()方法时,只是运行了®2,并没有进入if语句,所以没调用真正执行中断的代码b.interrupt().

2.若调用sleep()而使线程处于阻塞状态,这时调用interrupt()方法,会抛出InterruptedException,从而使线程提前结束阻塞状态,退出阻塞代码。为什么?




 Java线程之中,一个线程的生命周期分为:初始、就绪、运行、阻塞以及结束。当然,其中也可以有四种状态,初始、就绪、运行以及结束。

         一般而言,可能有三种原因引起阻塞:等待阻塞、同步阻塞以及其他阻塞(睡眠、jion或者IO阻塞);对于Java而言,等待阻塞是调用wait方法产生的,同步阻塞则是由同步块(synchronized)产生的,睡眠阻塞是由sleep产生的,jion阻塞是由jion方法产生的。

         言归正传,要中断一个Java线程,可调用线程类(Thread)对象的实例方法:interrupte();然而interrupte()方法并不会立即执行中断操作;具体而言,这个方法只会给线程设置一个为true的中断标志(中断标志只是一个布尔类型的变量),而设置之后,则根据线程当前的状态进行不同的后续操作。如果,线程的当前状态处于非阻塞状态,那么仅仅是线程的中断标志被修改为true而已;如果线程的当前状态处于阻塞状态,那么在将中断标志设置为true后,还会有如下三种情况之一的操作:

 

  • 如果是wait、sleep以及jion三个方法引起的阻塞,那么会将线程的中断标志重新设置为false,并抛出一个InterruptedException;
  • 如果是java.nio.channels.InterruptibleChannel进行的io操作引起的阻塞,则会对线程抛出一个ClosedByInterruptedException;(待验证)
  • 如果是轮询(java.nio.channels.Selectors)引起的线程阻塞,则立即返回,不会抛出异常。(待验证)
    如果在中断时,线程正处于非阻塞状态,则将中断标志修改为true,而在此基础上,一旦进入阻塞状态,则按照阻塞状态的情况来进行处理;例如, 一个线程在运行状态中,其中断标志被设置为true,则此后,一旦线程调用了wait、jion、sleep方法中的一种,立马抛出一个InterruptedException,且中断标志被清除,重新设置为false。
    通过上面的分析,我们可以总结, 调用线程类的interrupted方法,其本质只是设置该线程的中断标志,将中断标志设置为true,并根据线程状态决定是否抛出异常。因此,通过interrupted方法真正实现线程的中断原理是: 开发人员根据中断标志的具体值,来决定如何退出线程。
   一个简单的实现方式如下:

    public void run() {  
                try {  
                    while (true){  
                        Thread.sleep(1000l);//阻塞状态,线程被调用了interrupte()方法,清除中断标志,抛出InterruptedException  
                        //dosomething  
                        boolean isIn = this.isInterrupted();  
                        //运行状态,线程被调用了interrupte()方法,中断标志被设置为true  
                        //非阻塞状态中进行中断线程操作  
                        if(isIn) break;//退出循环,中断进程  
                    }  
                }catch (InterruptedException e){//阻塞状态中进行中断线程操作  
                    boolean isIn = this.isInterrupted();//退出阻塞状态,且中断标志被清除,重新设置为false,所以此处的isIn为false  
                    return;//退出run方法,中断进程  
                }  
            }  


        分别考虑了阻塞状态中进行中断线程和非阻塞状态中中断线程的处理方式。

 

        最后,说明一下interrupte方法的调用,该方法可在需要中断的线程本身中调用,也可在其他线程中调用需要中断的线程对象的该方法。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值