5

In the Java tutorial it says about try { ... } finally { ... }:

Note: If the JVM exits while the try or catch code is being executed, then the finally block may not execute. Likewise, if the thread executing the try or catch code is interrupted or killed, the finally block may not execute even though the application as a whole continues.

Is it true that a thread can be interrupted or killed (I thought that was impossible?) such that the finally block will not be executed while the JVM running this thread is not exited/killed? (I am puzzled because the quote above is pretty explicit about this, not much room for misunderstanding.)

Edit: Broke the question down to its core intend.

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192
  • There could be hundreds of `try-catch-finally` blocks written in the application servlet. What do you exactly mean with your question? – Luiggi Mendoza Oct 21 '13 at 17:33
  • I want to know how it is possible that the code within `finally` is not executed when the corresponding `try` block was already entered when a JVM is not shut down. From the quote, I understand that this is possible for threads even without calling `Thread.kill`. I wonder if servlet containers have a way of shutting down a web application such that I cannot rely on `finally` being executed. – Rafael Winterhalter Oct 21 '13 at 17:35
  • possible duplicate of [Does finally always execute in Java?](http://stackoverflow.com/questions/65035/does-finally-always-execute-in-java) – Luiggi Mendoza Oct 21 '13 at 17:36
  • 1
    As explained in the tutorial and in the possible dup Q/A, the only way `finally` is **not** executed is that the JVM stops. This is, by executing `System.exit(0);` or the JVM crashes. – Luiggi Mendoza Oct 21 '13 at 17:37
  • Well, I am mostly wondering about the part of the quote where it says that if the thread executing the try or catch code is **interrupted** would not execute a `finally` block. I understanding that exiting or killing a JVM will simply end the JVM process. – Rafael Winterhalter Oct 21 '13 at 17:38
  • Refer to http://stackoverflow.com/q/12430642/1065197 – Luiggi Mendoza Oct 21 '13 at 17:40
  • Agree this is a duplcate. OP, no, Thread.interrupt() will not stop a finally block from executing. – Radiodef Oct 21 '13 at 17:41
  • In other words, the official tutorial by Oracle is wrong: docs.oracle.com/javase/tutorial/essential/exceptions/finally.html? I am specifically asking about the issue of **interruption** where the JVM is **not** terminated. – Rafael Winterhalter Oct 21 '13 at 17:43
  • You can test what you've read in the link posted in my last comment where your question is covered. – Luiggi Mendoza Oct 21 '13 at 17:47
  • @LuiggiMendoza I just tested it and yes, finally is always executed regardless of Thread.interrupt(). – Radiodef Oct 21 '13 at 17:48
  • I want to believe you and I would also say that this is true if I would be asked. After discovering this quote in the tutorial I was however wondering if there might be a special circumstance where this is not true. For example, when a Java application starts another Java application such as in a servlet container. Maybe there is a way that the servlet container signals some sort of hard interrupt to the application. But maybe the tutorial is just wrong. – Rafael Winterhalter Oct 21 '13 at 17:51
  • An [`InterruptedException`](http://docs.oracle.com/javase/7/docs/api/java/lang/InterruptedException.html) maybe? – Luiggi Mendoza Oct 21 '13 at 17:52
  • An `InterruptedException` is just another exception. By my understanding of Java, it should not be possible that an `InterruptedException` skips a `finally` block. – Rafael Winterhalter Oct 21 '13 at 17:55
  • OK, further testing reveals that if a thread is interrupted while inside a finally block, the finally block is indeed exited without completing. – Radiodef Oct 21 '13 at 17:55
  • @Radiodef it is because the code inside the `finally` can throw another Exception and looks like you're not handling it =\. OP, again, the only ways to skip the `finally` block execution is: calling `System.exit(0);` in `try` (or in `catch`) block, or if the JVM crashes before the execution of the `finally`. – Luiggi Mendoza Oct 21 '13 at 17:57
  • And still, Oracle states that this is possible **even though** the application as a whole **continues**. @Radiodef: Did you block the thread with `Thread.sleep` and interrupt it than to synchronize the calls? Then your thread threw an unhandled exception. – Rafael Winterhalter Oct 21 '13 at 17:58
  • Well, I just saw someone mentioned the quote above in a comment to http://stackoverflow.com/questions/12430642/what-are-the-circumstances-under-which-a-finally-block-will-not-execute. I still wonder if there is an edge case. I wish I knew why this is written in the documentation. – Rafael Winterhalter Oct 21 '13 at 18:14
  • 1
    @LuiggiMendoza Yes. But the point is that yeah finally can be interrupted (if you are doing something stupid and insane). OP see my answer. Maybe that helps. – Radiodef Oct 21 '13 at 18:17

2 Answers2

3

Well, I stand corrected. It is possible by using deprecated methods:

@Test
public void testThread() throws Exception {
    Thread thread = new Thread(new MyRunnable());
    thread.start();
    Thread.sleep(100);
    thread.suspend();
    Thread.sleep(2000);
}

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Start");
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("Done");
        }
    }
}

Due to the pausing which will (most likely) occure while the thread is asleep, the finally block will never be executed.

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192
2

Rafael, I believe this is one of the edge cases you are after. If a thread is blocked on something native (eg reading from STDIN or a Socket), and the JVM is in a state of shutdown, and the thread is interrupted, then finally may not be invoked.

The following example indicates this without invoking deprecated methods:

  1. Sleep - finally is invoked.
  2. SystemIn - finally is not invoked.

The example is very contrived, and is purely provided for demonstrative purposes :)

public class Interrupted {

  static final List<Thread> THREADS = Arrays.asList(
      new Thread(new Sleep()),
      new Thread(new SystemIn())
  );
  static final CountDownLatch LATCH = new CountDownLatch(THREADS.size());

  public static void main(String[] args) throws Exception {
    Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownHook()));
    for (Thread thread : THREADS) {
      thread.start();
    }
    System.out.println("[main] Waiting for threads to start...");
    LATCH.await();
    System.out.println("[main] All started, time to exit");
    System.exit(0);
  }

  static abstract class BlockingTask implements Runnable {
    @Override
    public void run() {
      final String name = getClass().getSimpleName();
      try {
        LATCH.countDown();
        System.out.printf("[%s] is about to block...%n",name);
        blockingTask();
      } catch (Throwable e) {
        System.out.printf("[%s] ", name);
        e.printStackTrace(System.out);
      } finally {
        System.out.printf("[%s] finally%n", name);
      }
    }
    abstract void blockingTask() throws Throwable;
  }

  static class Sleep extends BlockingTask {
    @Override
    void blockingTask() throws Throwable {
      Thread.sleep(60 * 60 * 1000); // 1 hour
    }
  }

  static class SystemIn extends BlockingTask {
    @Override
    void blockingTask() throws Throwable {
      System.in.read();
    }
  }

  static class ShutdownHook implements Runnable {
    @Override
    public void run() {
      System.out.println("[shutdown-hook] About to interrupt blocking tasks...");
      for (Thread thread : THREADS) {
        thread.interrupt();
      }
      System.out.println("[shutdown-hook] Interrupted");
      try {
        for (int i=0; i<10; i++) {
          Thread.sleep(50L);
          System.out.println("[shutdown-hook] Still exiting...");
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

}
Muel
  • 4,309
  • 1
  • 23
  • 32
  • Isn't this just the OS pulling the rug? [the underlying operating system may only allow a fixed amount of time in which to shut down and exit](https://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html#addShutdownHook(java.lang.Thread)) – Chomeh May 12 '15 at 08:13
  • I don't think so. The code above triggers the interrupt immediately, the OS isn't waiting (much) at that point in time. Plus I keep the shut-down hook alive for sometime afterwards. – Muel May 13 '15 at 04:58
  • If you don't interrupt at all, finally still isn't called. exit() won't wait 1hr for Sleep, it just gets killed. Anyway I learnt something about CountDownLatch, Cheers! – Chomeh May 13 '15 at 12:45