【问题标题】:Interrupting Runnable instance with its method用它的方法中断 Runnable 实例
【发布时间】:2019-09-25 18:47:10
【问题描述】:

假设我有这样的课程: Foo.java:

public class Foo implements Runnable{
  public void run(){
    try{
      sleep(3000);
      System.out.println("Slept for 3s");
    }catch(InterruptedException e){
      Thread.currentThread().interrupt();
      System.out.println("Exception handled from Foo");
    }
  }

  public void terminate(){
    Thread.currentThread().interrupt();
  }
}

Bar.java:

public class Bar implements Runnable{
  private Foo f;

  public Bar(Foo f){
    this.f = f;
  }

  public void run(){
    System.out.println("Interrupting Foo");
    f.terminate();
    System.out.println("Interrupted Foo");
    try{
      sleep(4000);
      System.out.println("Slept for 4s");
    }catch(InterruptedException e){
      Thread.currentThread().interrupt();
      System.out.println("Exception handled from Bar");
    }
  }
}

Test.java:

public class Test{
  public static void main(String[] args){
    Foo f = new Foo();
    Bar b = new Bar(f);
    new Thread(f).start();
    new Thread(b).start();
    System.out.println("Test end");
  }
}

当我编写这段代码时,我希望输出如下:

Test end
Interrupting Foo
Exception from Foo handled
Interrupted
Slept for 4s

但我得到的不是上面的:

Test end
Interrupting Foo
Interrupted
Exception from Bar handled
Slept for 3s

这段代码背后的故事是,在我的应用程序中,我需要一个 Thread/Runnable 来中断另一个运行我有权访问的另一个 Runnable 实例的匿名线程。知道我可以使用 Thread.currentThread.interrupt() 从 Runnable 内部中断一个线程(从 Internet 学习)我认为在 Runnable 的方法中调用这个我想停止,然后在另一个线程的实例上调用这个方法会打断它。但是如上面的示例所示,它正在中断调用该方法的线程,而不是运行定义该方法的 Runnable 的线程。知道我对 Thread.currentThread.interrupt() 的工作原理一无所知,而且它没有按我预期的那样工作,我有几个问题:
1. 从这个例子的工作原理来看,我假设 Thread.currentThread.interrupt() 会中断正在执行调用它的函数的线程,而不是正在运行调用函数的实例的线程。那正确吗?如果不是,它是如何工作的?
2.(最重要的)有没有办法通过从另一个线程调用它的Runnable的方法来中断一个线程?如果是 - 它是如何完成的?如果没有 - 我是否必须有权访问 Thread 实例才能中断它,或者如果 Foo 只是扩展 Thread 而不是实现 Runnable 会更好吗?
3. 为什么在睡眠前调用中断会捕获Bar的异常?

【问题讨论】:

    标签: java multithreading interrupt runnable


    【解决方案1】:

    尝试全面回答您的问题:

    1. 你是对的。正如您在此处看到的那样,https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html Thread.currentThread() 返回对当前执行线程的引用。

    2. 是的,有。 Android: How do I stop Runnable?

    3. 因为正如您在 1. 中建议的那样,您正在中断当前正在执行的线程,这是 Bar 上下文所在的线程。Bar 导致它自己的 InterruptedException

      李>

    【讨论】:

    • 其实我用的不是Android,而是纯Java。无论如何,我遇到了您为 2 建议的解决方案。但第一个是针对 Android 的,如果线程被锁定在 sleep()、wait() 或 I/O 操作(例如等待用户输入)上,则另一个是没有用的。在那种情况下呢?而且我知道 Bar 导致它自己的 InterruptedException 但是如果在进入 try 块之前抛出它,为什么它会捕获它?
    • 我发布的答案对我来说效果很好,是什么让您认为它是“适用于 Android”?语言不会随设备而改变。异常处理由异常处理程序完成,由每个 catch 块调用,因此实际上不是由您中断的线程完成:docs.oracle.com/javase/tutorial/essential/exceptions/catch.html。以任何其他方式这样做都会破坏异常的目的,因为这些错误应该是不能被忽略的。
    • 对不起,起初我认为 ExecutorService 是来自 Android API 的东西,但在阅读了它之后,我发现使用它确实是解决这个问题的一种方法。谢谢。
    【解决方案2】:

    Thread.currentThread() 返回当前运行的线程。您似乎期望有一个与Foo 的实例关联的线程,因为它的run() 方法几乎占据了整个类,然后它的terminate() 方法将使用该线程,但Foo 是只是一个普通的班级。它的run() 方法可以由一个不同的线程调用,该线程已经用它的一个实例启动,也可以直接调用它。无论如何,当在某个线程上直接调用terminate() 方法时,Thread.currentThread() 将返回该线程。

    thread.interrupt() 方法不一定会导致抛出 InterruptedException。如果线程正在休眠、等待或调用某些阻塞操作,则会发生这种情况,但文档说:

    如果前面的条件都不成立,则设置该线程的中断状态。

    中断状态只是一个标志,表明线程已被中断。下次调用 sleep、wait 或阻塞操作时,将立即抛出 InterruptedException

    如果您想中断特定线程,而不是当前线程,特别是对于您已启动的线程,有一个简单的解决方案:在启动时存储对该线程的引用。

    Thread fthread = new Thread(f).start();
    Bar b = new Bar(f, fthread);
    

    然后在Bar,而不是调用f.terminate(),调用fthread.interrupt()

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-08-19
      • 2011-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多