Domanda

There's a bit of a clash for whether shutdown hooks, which are of class Thread, run their runnable code on the thread on which the shutdown was called, or run on themselves.

addShutdownHook takes a Thread as a parameter. This implies the thread will start and run its run method on itself. This is also consistent with the documentation for addShutdownHook:

public void addShutdownHook(Thread hook)

Registers a new virtual-machine shutdown hook. The Java virtual machine shuts down in response to two kinds of events:

  • The program exits normally, when the last non-daemon thread exits or when the exit (equivalently, System.exit) method is invoked, or
  • The virtual machine is terminated in response to a user interrupt, such as typing ^C, or a system-wide event, such as user logoff or system shutdown.

A shutdown hook is simply an initialized but unstarted thread. When the virtual machine begins its shutdown sequence it will start all registered shutdown hooks in some unspecified order and let them run concurrently. When all the hooks have finished it will then run all uninvoked finalizers if finalization-on-exit has been enabled. Finally, the virtual machine will halt. Note that daemon threads will continue to run during the shutdown sequence, as will non-daemon threads if shutdown was initiated by invoking the exit method.

(Emphasis mine)

However, the code is as follows:

/* Run all registered shutdown hooks
 */
private static void runHooks() {
    for (int i=0; i < MAX_SYSTEM_HOOKS; i++) {
        try {
            Runnable hook;
            synchronized (lock) {
                // acquire the lock to make sure the hook registered during
                // shutdown is visible here.
                currentRunningHook = i;
                hook = hooks[i];
            }
            if (hook != null) hook.run();  // not Thread.start - Runnable.run (!!)
        } catch(Throwable t) {
            if (t instanceof ThreadDeath) {
                ThreadDeath td = (ThreadDeath)t;
                throw td;
            }
        }
    }
}

(!! comment mine)

Note this is little changed from JDK 6, which makes the problem all the clearer:

/* Run all registered shutdown hooks
 */
private static void runHooks() {
    /* We needn't bother acquiring the lock just to read the hooks field,
     * since the hooks can't be modified once shutdown is in progress
     */
    for (Runnable hook : hooks) {
        try {
            hook.run();
        } catch(Throwable t) {
            if (t instanceof ThreadDeath) {
                ThreadDeath td = (ThreadDeath)t;
                throw td;
            }
        }
    }
}

At first I thought I was reading this wrong and calling run magically started the thread. But it doesn't. I wrote the run code myself. That code does not start a thread (in the case of a Thread, it's natural and correct to assume run runs on the thread.)

So something is really wrong here. Is it the Javadoc and the signature of the addShutdownHook method, which per the code should not be taking a thread, but a runnable? Is it the implementation? Or is it the more likely culprit - me; and if so, how?

È stato utile?

Soluzione

You are confusing Shutdown.runHooks() and ApplicationShutdownHooks.runHooks(). The shutdown hooks you register with Runtime are registered with ApplicationShutdownHooks, which itself registers a Runnable as a Shutdown hook

static {
    try {
        Shutdown.add(1 /* shutdown hook invocation order */,
            false /* not registered if shutdown in progress */,
            new Runnable() {
                public void run() {
                    runHooks(); // (!!)  your hooks
                }
            }
        );
        hooks = new IdentityHashMap<>();
    } catch (IllegalStateException e) {
        // application shutdown hooks cannot be added if
        // shutdown is in progress.
        hooks = null;
    }
}

The shutdown hooks are ran concurrently

static void runHooks() { // In ApplicationShutdownHooks
    Collection<Thread> threads;
    synchronized(ApplicationShutdownHooks.class) {
        threads = hooks.keySet();
        hooks = null;
    }

    for (Thread hook : threads) { // (!!) your hooks
        hook.start();
    }
    for (Thread hook : threads) {
        try {
            hook.join();
        } catch (InterruptedException x) { }
    }
}

For reference, the (oracle jdk7) code for Runtime#addShutdownHook(Thread).

public void addShutdownHook(Thread hook) {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        sm.checkPermission(new RuntimePermission("shutdownHooks"));
    }
    ApplicationShutdownHooks.add(hook);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top