Java线程中断的本质深入理解

参考1:http://www.51ou.com/browse/java/21131_2.html

参考2:http://blog.csdn.net/wxwzy738/article/details/8516253

一、引入

   java多线程中断有Stop和interrupt,此处不谈LockSupport。

1、Stop是立即将一个线程中断,并释放锁,如果线程刚好执行到一半,很容易引起数据不一致。对程序危害很大,因此被放弃不用。当然如果你很清楚的知道自己在干什么,你才可以用stop来终止线程。

2、interrupt

public static boolean interrupted 得到当前的线程标志位的状态,返回该状态,并将该状态设置为 false(默认状态)

public boolean isInterrupted() 测试线程是否已经中断。线程的中断状态 不受该方法的影响。

public void interrupt()     (只是设置中断标志位,并不一定中断线程,让线程自己决定是否中断,此处建议最好的中断方式是 设置bool变量,自己控制代码如下:

class Example2 extends Thread {
    volatile boolean stop = false;
    public static void main( String args[] ) throws Exception 
   {
       Example2 thread = new Example2();
       System.out.println( "Starting thread..." );
       thread.start();
       Thread.sleep( 3000 );
       System.out.println( "Asking thread to stop..." );

       thread.stop = true;
       Thread.sleep( 3000 );
       System.out.println( "Stopping application..." );
      //System.exit( 0 );
   }

  public void run() {
      while ( !stop ) {
          System.out.println( "Thread is running..." );
          long time = System.currentTimeMillis();
          while ( (System.currentTimeMillis()-time < 1000) && (!stop) ) {
      }
    }
   System.out.println( "Thread exiting under request..." );
  }
}

      上面列出了与中断有关的几个方法及其行为,可以看到interrupt是中断线程。如果不了解Java的中断机制,这样的一种解释极容易造成误解,认为调用了线程的interrupt方法就一定会中断线程。 
其实,Java的中断是一种协作机制。也就是说调用线程对象的interrupt方法并不一定就中断了正在运行的线程,它只是要求线程自己在合适的时机中断自己。每个线程都有一个boolean的中断状态(不一定就是对象的属性,事实上,该状态也确实不是Thread的字段),interrupt方法仅仅只是将该状态置为true 

public class TestInterrupt { 
public static void main(String[] args) { 
   Thread t = new MyThread(); 
   t.start(); 
   t.interrupt(); 
   System.out.println("
已调用线程的interrupt方法"); 

static class MyThread extends Thread { 
public void run() { 
int num = longTimeRunningNonInterruptMethod(2, 0); 
System.out.println("
长时间任务运行结束,num=" + num); 
System.out.println("
线程的中断状态:" + Thread.interrupted()); 

private static int longTimeRunningNonInterruptMethod(int count, int initNum) { 
for(int i=0; i<count; i++) { 
for(int j=0; j<Integer.MAX_VALUE; j++) { 
initNum ++; 


return initNum; 



一般情况下,会打印如下内容: 
已调用线程的interrupt方法 
长时间任务运行结束,num=-2 
线程的中断状态:true 
可见,interrupt方法并不一定能中断线程。但是,如果改成下面的程序,情况会怎样呢? 


import java.util.concurrent.TimeUnit; 
public class TestInterrupt { 
public static void main(String[] args) { 
Thread t = new MyThread(); 
t.start(); 
t.interrupt(); 
System.out.println("
已调用线程的interrupt方法"); 

static class MyThread extends Thread { 
public void run() { 
int num = -1; 
try { 
num = longTimeRunningInterruptMethod(2, 0); 
} catch (InterruptedException e) { 
System.out.println("
线程被中断"); 
throw new RuntimeException(e); 

System.out.println("
长时间任务运行结束,num=" + num); 
System.out.println("
线程的中断状态:" + Thread.interrupted()); 

private static int longTimeRunningInterruptMethod(int count, int initNum)throws InterruptedException{ 
for(int i=0; i<count; i++) { 
TimeUnit.SECONDS.sleep(5); 

return initNum; 



经运行可以发现,程序抛出异常停止了,run方法里的后两条打印语句没有执行。那么,区别在哪里? 
一般说来,如果一个方法声明抛出InterruptedException,表示该方法是可中断的(没有在方法中处理中断却也声明抛出InterruptedException的除外),也就是说可中断方法会对interrupt调用做出响应(例如sleep响应interrupt的操作包括清除中断状态,抛出InterruptedException),如果interrupt调用是在可中断方法之前调用,可中断方法一定会处理中断,像上面的例子,interrupt方法极可能在run未进入sleep的时候就调用了,但sleep检测到中断,就会处理该中断。如果在可中断方法正在执行中的时候调用interrupt,会怎么样呢?这就要看可中断方法处理中断的时机了,只要可中断方法能检测到中断状态为true,就应该处理中断。让我们为开头的那段代码加上中断处理。 
那么自定义的可中断方法该如何处理中断呢?那就是在适合处理中断的地方检测线程中断状态并处理。 


public class TestInterrupt { 
public static void main(String[] args) throws Exception { 
Thread t = new MyThread(); 
t.start(); 
// TimeUnit.SECONDS.sleep(1);//
如果不能看到处理过程中被中断的情形,可以启用这句再看看效果 
t.interrupt(); 
System.out.println("
已调用线程的interrupt方法"); 

static class MyThread extends Thread { 
public void run() { 
int num; 
try { 
num = longTimeRunningNonInterruptMethod(2, 0); 
} catch (InterruptedException e) { 
throw new RuntimeException(e); 

System.out.println("
长时间任务运行结束,num=" + num); 
System.out.println("
线程的中断状态:" + Thread.interrupted()); 

private static int longTimeRunningNonInterruptMethod(int count, int initNum)throws InterruptedException { 
if(interrupted()) { 
throw new InterruptedException("
正式处理前线程已经被请求中断"); 

for(int i=0; i<count; i++) { 
for(int j=0; j<Integer.MAX_VALUE; j++) { 
initNum ++; 

//
假如这就是一个合适的地方 
if(interrupted()) { 
//
回滚数据,清理操作等 
throw new InterruptedException("
线程正在处理过程中被中断"); 


return initNum; 



如上面的代码,方法longTimeRunningMethod此时已是一个可中断的方法了。在进入方法的时候判断是否被请求中断,如果是,就不进行相应的处理了;处理过程中,可能也有合适的地方处理中断,例如上面最内层循环结束后。 
这段代码中检测中断用了Thread的静态方法interrupted,它将中断状态置为false,并将之前的状态返回,而isInterrupted只是检测中断,并不改变中断状态。一般来说,处理过了中断请求,应该将其状态置为false。但具体还要看实际情形。 

二、Java中断的本质 
在历史上,Java试图提供过抢占式限制中断,但问题多多,例如已被废弃的Thread.stopThread.suspend Thread.resume等。另一方面,出于Java应用代码的健壮性的考虑,降低了编程门槛,减少不清楚底层机制的程序员无意破坏系统的概率。 
如今,Java的线程调度不提供抢占式中断,而采用协作式的中断。其实,协作式的中断,原理很简单,就是轮询某个表示中断的标记,我们在任何普通代码的中都可以实现。例如下面的代码:

volatile bool isInterrupted; 
//… 
while(!isInterrupted) { 
compute(); 


但是,上述的代码问题也很明显。当compute执行时间比较长时,中断无法及时被响应。另一方面,利用轮询检查标志变量的方式,想要中断waitsleep等线程阻塞操作也束手无策。 
如果仍然利用上面的思路,要想让中断及时被响应,必须在虚拟机底层进行线程调度的对标记变量进行检查。是的,JVM中确实是这样做的。下面摘自java.lang.Thread的源代码: 


public static boolean interrupted() { 
return currentThread().isInterrupted(true); 

//… 
private native boolean isInterrupted(boolean ClearInterrupted); 


可以发现,isInterrupted被声明为native方法,取决于JVM底层的实现。 
实际上,JVM内部确实为每个线程维护了一个中断标记。但应用程序不能直接访问这个中断变量,必须通过下面几个方法进行操作: 


public class Thread { 
//
设置中断标记 
public void interrupt() { ... } 
//
获取中断标记的值 
public boolean isInterrupted() { ... } 
//
清除中断标记,并返回上一次中断标记的值 
public static boolean interrupted() { ... } 
... 


通常情况下,调用线程的interrupt方法,并不能立即引发中断,只是设置了JVM内部的中断标记。因此,通过检查中断标记,应用程序可以做一些特殊操作,也可以完全忽略中断。

你可能想,如果JVM只提供了这种简陋的中断机制,那和应用程序自己定义中断变量并轮询的方法相比,基本也没有什么优势。

JVM
内部中断变量的主要优势,就是对于某些情况,提供了模拟自动中断陷入的机制。 
在执行涉及线程调度的阻塞调用时(例如waitsleepjoin),如果发生中断,被阻塞线程会尽可能快的抛出InterruptedException。因此,我们就可以用下面的代码框架来处理线程阻塞中断: 


try { 
//wait
sleepjoin 

catch(InterruptedException e) { 
//
某些中断处理工作 


所谓尽可能快,我猜测JVM就是在线程调度调度的间隙检查中断变量,速度取决于JVM的实现和硬件的性能。 

三、一些不会抛出 InterruptedException 的线程阻塞操作 
然而,对于某些线程阻塞操作,JVM并不会自动抛出InterruptedException异常。例如,某些I/O操作和内部锁操作。对于这类操作,可以用其他方式模拟中断: 
1
java.io中的异步socket I/O 
读写socket的时候,InputStreamOutputStreamreadwrite方法会阻塞等待,但不会响应java中断。不过,调用Socketclose方法后,被阻塞线程会抛出SocketException异常。 
2
)利用Selector实现的异步I/O 
如果线程被阻塞于Selector.select(在java.nio.channels中),调用wakeup方法会引起ClosedSelectorException异常。 
3
)锁获取 
如果线程在等待获取一个内部锁,我们将无法中断它。但是,利用Lock类的lockInterruptibly方法,我们可以在等待锁的同时,提供中断能力。 

四、两条编程原则 
另外,在任务与线程分离的框架中,任务通常并不知道自身会被哪个线程调用,也就不知道调用线程处理中断的策略。所以,在任务设置了线程中断标记后,并不能确保任务会被取消。因此,有以下两条编程原则: 
1
)除非你知道线程的中断策略,否则不应该中断它。 
这条原则告诉我们,不应该直接调用Executer之类框架中线程的interrupt方法,应该利用诸如Future.cancel的方法来取消任务。

2
)任务代码不该猜测中断对执行线程的含义。 
这条原则告诉我们,一般代码遇在到InterruptedException异常时,不应该将其捕获后吞掉,而应该继续向上层代码抛出。 
总之,Java中的非抢占式中断机制,要求我们必须改变传统的抢占式中断思路,在理解其本质的基础上,采用相应的原则和模式来编程。   


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕业设计,基于SpringBoot+Vue+MySQL开发的体育馆管理系统,源码+数据库+毕业论文+视频演示 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本体育馆管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此体育馆管理系统利用当下成熟完善的SpringBoot框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的Mysql数据库进行程序开发。实现了用户在线选择试题并完成答题,在线查看考核分数。管理员管理收货地址管理、购物车管理、场地管理、场地订单管理、字典管理、赛事管理、赛事收藏管理、赛事评价管理、赛事订单管理、商品管理、商品收藏管理、商品评价管理、商品订单管理、用户管理、管理员管理等功能。体育馆管理系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。 关键词:体育馆管理系统;SpringBoot框架;Mysql;自动化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值