Detecting that JVM is shutting down

2019-07-17 16:09发布

问题:

I have a Swing application that handles Ctrl+C using addShutdownHook(), and it works fine until one of the shutdown tasks I have calls a function that under normal circumstances changes a JLabel text, at which point it hangs.

I assume the problem is that the Swing EDT has either terminated or is waiting for something.

Is there a way to either determine that the EDT has terminated or is "done" (so I can avoid calling Swing methods), or to prevent the usual close-all-the-windows-down behavior on Ctrl-C?


Just to clarify:

I have a method in a class called stop(). Under normal circumstances this can get called (along with its complement start()) and it triggers a cascade of things that causes a JLabel to be updated, for visual feedback that a stop() has occurred.

When my shutdown hook runs, I need to call stop() to gracefully shutdown some resources.

What I'm asking is how I can detect that Swing EDT is not there, so I can rewrite stop() so that it detects a lack of Swing and avoids the call to the Swing functions.

回答1:

The following hack helps to determine whether JVM is in the process of shutting down without passing any flags around:

private static final Thread DUMMY_HOOK = new Thread();

public static boolean isShuttingDown()
{
    try {
        Runtime.getRuntime().addShutdownHook(DUMMY_HOOK);
        Runtime.getRuntime().removeShutdownHook(DUMMY_HOOK);
    } catch ( IllegalStateException e ) {
        return true;
    }

    return false;
}

But don't forget about possible concurrency issues.



回答2:

Shutdown hooks should not expect other services to be in a known state (e.g. the UI event-handling thread, etc.), because the application has been requested to be shut down. Writing a shutdown hook that tries to update something that isn't 100% within the hook's control can result in this, and other behavior, that will be difficult to troubleshoot.

The addShutdownHook method specifically mentions this case, and warns that you shouldn't try these types of actions, since doing so can easily result in unexpected consequences, such as the lockup you've observed.

From the documentation:

Shutdown hooks run at a delicate time in the life cycle of a virtual machine and should therefore be coded defensively. They should, in particular, be written to be thread-safe and to avoid deadlocks insofar as possible. They should also not rely blindly upon services that may have registered their own shutdown hooks and therefore may themselves in the process of shutting down.

In this case, since Swing is a multi-threaded UI system which you do not control, I would recommend not trying to alter anything in the UI at the shutdown stage of you're program's life. Doing so will result in strange, unpredictable, and sometimes non-repeatable situations that can vary from one run to the next, or from one machine to the next, depending on how the threads get scheduled to shutdown. How, and when, another thread stops its work is not designed to happen in a linear, predictable way. As such, any code you write in a multi-threaded program that relies upon another thread being in a certain state at a certain time (unless those threads are clearly communicating with each other about their state), will open you up to these kinds of issues.

I suppose my follow up question would be, "Why do you want/need to alter the JLabel during the shutdown process?" If you want to change the state of something before shutdown, the better way to do it would be catch the keyboard input as a regular event (preventing it from causing the application to close), change the JLabel text, and then start a shutdown of the application yourself via a call to System.runFinalization() and/or System.ext(int)



回答3:

I ended up setting a flag at the beginning of the shutdown hook, and communicating this (via objects set up ahead of time) to the object with my stop() method, so that it can test this flag and decide whether or not to call the Swing methods.

If the shutdown flag is not set, the Swing methods get called. Otherwise they don't.