-1

Let's say I have classes like those: 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");
  }
}

When I wrote this code I expected the output to be like:

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

But instead of above I got:

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

Story behind this code is that in my application I need one Thread/Runnable to interrupt another anonymous Thread running another Runnable instance to which I have access. Knowing that I could interrupt a Thread from inside Runnable with Thread.currentThread.interrupt() (learned from the Internet) I thought that calling this in a method of the Runnable I want to stop and then calling this method on its instance in another Thread would interrupt it. But as shows the example above it is interrupting the Thread which called the method instead of Thread that runs the Runnable in which this method is defined. Knowing that I know nothing about how Thread.currentThread.interrupt() is working and that it's not working as I expected I have a few questions:
1. From how this example works I assume that Thread.currentThread.interrupt() interrupts the Thread that is executing the function in which it is called and not the one that is running the instance on which the function was called. Is that right? If not how does it work?
2. (most important) Is there a way to interrupt a Thread by calling a method of its Runnable from another Thread? If yes - how is it done? If not - do I have to have access to Thread instance to interrupt it or would it be better if Foo just extended Thread instead of implementing Runnable?
3. Why is exception from Bar catched if the interrupt is called before sleep?

Shin Q'dan
  • 81
  • 1
  • 7

2 Answers2

1

To try and answer your question fully:

  1. You are right. As you can see here https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html Thread.currentThread() returns a reference to the currently executing thread.

  2. Yes there is. Android: How do I stop Runnable?

  3. Because as you suggested in 1. you are interrupting the currently executing thread, which is the one the Bar context is in. Bar is causing it's own InterruptedException.

Lenny Eing
  • 146
  • 2
  • 8
  • Actually I'm not on Android but pure Java. Anyway, I came across solution you suggested for 2. but first one is for Android and the other is of no use if the Thread is locked on sleep(), wait() or I/O operation such as waiting for user input. What in that case? And I understand that Bar is causing it's own InterruptedException but why it catches it if it's thrown before try block is entered? – Shin Q'dan Sep 25 '19 at 19:21
  • The answer I posted works just fine for me, what makes you think it is "for Android"? The language does not change with the device. Exception Handling is done by an exception handler, invoked with each catch-block and therefore not actually done by the Thread you interrupted: https://docs.oracle.com/javase/tutorial/essential/exceptions/catch.html. Doing it any other way would defeat the purpose of exceptions, since these are errors are supposed to be unable to be ignored. – Lenny Eing Sep 25 '19 at 19:46
  • Sorry, at first I thought the ExecutorService is something from Android API but after reading about it I see using it indeed is a way to solve this problem. Thanks. – Shin Q'dan Sep 25 '19 at 20:18
1

Thread.currentThread() returns the currently running thread. You seem to be expecting that there is a thread associated with the instance of Foo, since its run() method occupies almost the entirety of the class, and its terminate() method would then use that thread, but Foo is just an ordinary class. Its run() method could be called by a different thread that has been started with an instance of it, or it could be called directly. In any case, when the terminate() method is called directly on some thread, Thread.currentThread() will return that thread.

The thread.interrupt() method doesn't necessarily cause an InterruptedException to be thrown. That will happen if the thread is sleeping, waiting, or calling some blocking operation, but the documentation says:

If none of the previous conditions hold then this thread's interrupt status will be set.

The interrupt status is just a flag that indicates the thread has been interrupted. The next time sleep, wait, or a blocking operation is called, the InterruptedException will immediately be thrown.

If you want to interrupt a particular thread, and not the current thread, particularly for a thread you've started there's a simple solution: Store a reference to the thread when you start it.

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

And then in Bar, instead of calling f.terminate(), call fthread.interrupt().

David Conrad
  • 15,432
  • 2
  • 42
  • 54