【问题标题】:Java: How do I catch InterruptedException on a thread, when interrupted by another thread?Java:当被另一个线程中断时,如何在一个线程上捕获 InterruptedException?
【发布时间】:2012-09-01 19:15:49
【问题描述】:

我正在开发一个多线程应用程序来建立与外部服务器的连接 - 每个都在单独的线程上 - 并且在有输入之前将被阻止。这些中的每一个都扩展了 Thread 类。为了便于说明,我们将这些称为“连接线程”。

所有这些连接线程都存储在并发哈希图中。

然后,我允许 RESTful Web 服务方法调用来取消任何线程。 (我使用的是 Grizzly/Jersey,所以每个调用都是一个独立的线程。)

我检索特定的连接线程(从 hashmap 中)并在其上调用 interrupt() 方法。

那么,问题来了,在连接线程中,我如何捕获 InterruptedException? (当连接线程被外部 RESTful 命令停止时,我想做一些事情。)

【问题讨论】:

  • 好吧,AFAIK,当线程抛出 InterruptedEx 时。它将传播到启动异常抛出线程的线程。您可以在该父线程上捕获 IE。
  • 我刚刚做了一个实验,把启动连接线程的代码包围起来,编译器警告没有抛出InterruptedException。
  • 此外,启动这些连接线程的父线程不会进入等待或睡眠状态,因此这意味着(或至少我认为)它在启动所有这些子线程后不再存在.
  • “中断一个不活跃的线程不需要有任何效果”。 @spec JSR-51

标签: java multithreading runnable


【解决方案1】:

所以,这里的问题是,在连接线程中,我如何捕捉 中断异常?

你不能。因为如果您的线程在读取 I/O 操作时被阻塞,则它不能是 interrupted。这是因为interrupt 只是设置了一个标志来指示线程已被中断。但是,如果您的线程已被 I/O 阻塞,它将看不到该标志。
正确的方法是关闭底层套接字(线程被阻塞),然后捕获异常并向上传播。
因此,由于您的连接线程 extend Thread 执行以下操作:

@Override  
public void interrupt(){  
   try{  
      socket.close();  
   }  
   finally{  
     super.interrupt();  
   }  
}   

这样可以中断在 I/O 上阻塞的线程。

然后在您的run 方法中执行:

@Override  
public void run(){  

    while(!Thread.currentThread().isInterrupted()){    
       //Do your work

    }  
}    

所以在你的情况下,不要尝试catchInterruptedException。您不能中断在 I/O 上阻塞的线程。只需检查您的线程是否已被中断,并通过关闭流来促进中断。

【讨论】:

    【解决方案2】:

    当您在某个线程上调用Thread.interrupt() 时,会发生为该线程设置的“中断”标志。有些方法会检查这个标志(通过Thread.interrupted()Thread.isInterrupted())并抛出InterruptedException,但通常只有可以阻止的方法才能做到这一点。所以不能保证InterruptedException 会被抛出到被中断的线程中。如果您不调用任何抛出InterruptedException 的方法,那么捕获该异常是没有意义的,因为它根本不会被抛出。但是,您始终可以通过调用 Thread.isInterrupted() 来检查您的线程是否被中断。

    【讨论】:

    • 非常正确,需要调用mycontroller.getReadingThread().interrupt();
    【解决方案3】:

    阻塞的问题。

    悬停者,试试这个代码,也许它会帮助你:

    try{
     yourObject.read();
    }catch(InterruptedException ie){
    // interrupted by other thread
    }
    catch(Exception ex){
    // io or some other exception happent
    }
    

    你的读取方法,应该检查socket是否有可用的buytes,例如,如果没有读取它,否则进入speel模式。当睡眠比可用时唤醒(InterruptedException)在pur套接字读取(无论您读取什么)它将被阻止。某些 API 具有最大等待值,例如 5 秒 60 秒,如果没有读取任何内容,则将执行下一个代码。

    class MyReadingObject
    {
       public read() throws InterruptedException{
          while(shouldIread){
            if(socket.available() > 0){
               byte[] buff = new byte[socket.avaialble()]
               socket.read(buff);
               return;
            }
            else{
               Thread.currentThread.sleep(whateverMilliseconds);
            } 
         }
      }
    }
    

    类似的东西,但带有错误处理和一些设计模式

    【讨论】:

    • 我尝试过这样的事情,问题是“yourObject.read()”没有抛出 InterruptedException。编译器将显示一个错误,即代码中没有任何地方抛出该异常并拒绝编译。
    • 我知道我被否决了,我应该重新考虑我会在这里浪费多少时间。谢谢大家。乐于助人,祝你好运!
    • 不确定谁对你投了反对票,但我对你投了赞成票,因为我认为反对票不值得。我猜不赞成票是由于您的代码中的拼写错误/语法错误?
    【解决方案4】:

    在线程上调用interrupt() 不会停止它,它只是打开中断标志。代码负责处理所考虑的线程中断状态的变化并采取相应的行动。如果您在该线程中执行阻塞操作,那么您几乎就是 SOL,因为您的线程在读取时“阻塞”了。看看我发布的答案here。所以基本上,除非你在东西上循环或定期检查该线程内的一些标志,否则你无法在不关闭套接字或类似的东西的情况下爆发。

    这里的一个解决方案是“显式”公开底层连接对象并在其上调用close(),强制它抛出某种异常,然后可以在线程代码中处理。比如:

    class MyAction extends Thread implements Disposable {
    
      public void doStuff() {
        try {
          byte[] data = this.connection.readFully();
      } catch (InterruptedException e) {
        // possibly interrupted by forceful connection close    
      }
    
      @Override
      public void dispose() {
        this.connection.close();
      }
    }
    
    // Elsewhere in code
    MyAction action = conMap.get("something");
    action.dispose();
    

    【讨论】:

      【解决方案5】:

      像这样使用 try-catch:

      try {
          //code
      } catch ( InterruptedException e) {
          //interrupted
      }
      

      我认为这应该可以解决问题,您还可以保留一个布尔变量来决定是否退出,这样他们就会检查该变量,如果是真的,停止

      【讨论】:

      • 我把这个放在哪里?在 run() 方法中? - 编译器会说代码不会抛出 InterruptedException 并且不会编译。
      猜你喜欢
      • 2013-01-12
      • 1970-01-01
      • 2021-11-22
      • 2021-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多