背景
在某些情况下,我们总希望在java application退出之前做一些资源清除的操作。比如:线程池,在应用关闭后仍然存活,从而造成服务宕机。而java正好给我们提供了这样的方法来关闭这些资源,ShutdownHooks. 接下来就将其涉及到的源码逐一解释。
使用场景
程序正常退出
使用System.exit()
终端使用Ctrl+C触发的中断
系统关闭
OutOfMemory宕机
使用Kill pid命令干掉进程(注:在使用kill -9 pid时,是不会被调用的)
Shutdown
它是关闭jvm的一个直接类,如System.exit(0). 包含JNI方法。
class Shutdown { //不能直接调用此类。通常是通过Runtime.getRuntime().addShutdownHook(thread)间接使用。
// shutdown的几个状态
private static final int RUNNING = 0; // application正在运行
private static final int HOOKS = 1; // 正在处理hooks
private static final int FINALIZERS = 2; // 正在处理finalizers
private static int state = RUNNING; // 默认为运行状态
// hook的数量,10个。
private static final int MAX_SYSTEM_HOOKS = 10;
private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS];
private static int currentRunningHook = 0;
// 添加hook。
// slot表示hooks的下标,也就是执行hook的顺序。
// registerShutdownInProgress表示是否在shutdown执行过程添加hook,通常是false.
// hook表示的执行的线程。
static void add(int slot, boolean registerShutdownInProgress, Runnable hook) {
synchronized (lock) { // 避免并发情况下的覆盖。
if (hooks[slot] != null) // 如果hooks对应的槽点已经有对应的hook,也不能添加。
throw new InternalError("Shutdown hook at slot " + slot + " already registered");
if (!registerShutdownInProgress) { // 表示执行shutdown过程中,不添加hook.
if (state > RUNNING) // 如果已经在执行shutdown操作,则不能添加hook
throw new IllegalStateException("Shutdown in progress");
} else {
// 如果hooks已经执行完毕,则不能再添加hook。如果正在执行hooks时,添加的槽点小于当前执行的槽点位置,则也不能添加。
if (state > HOOKS || (state == HOOKS && slot <= currentRunningHook))
throw new IllegalStateException("Shutdown in progress");
}
hooks[slot] = hook; // 对应槽点增加hook.
}
}
// 执行所有注册的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();
} catch(Throwable t) {
if (t instanceof ThreadDeath) {
ThreadDeath td = (ThreadDeath)t;
throw td;
}
}
}
}
// 执行halt操作,也就是关闭JVM的操作
static void halt(int status) {
synchronized (haltLock) {
halt0(status);
}
}
static native void halt0(int status); // JNI 真正的halt方法
// Wormhole for invoking java.lang.ref.Finalizer.runAllFinalizers
private static native void runAllFinalizers();
// shutdown的执行顺序:runHooks > runFinalizersOnExit
private static void sequence() {
synchronized (lock) {
if (state != HOOKS) return;
}
runHooks();
boolean rfoe;
synchronized (lock) {
state = FINALIZERS;
rfoe = runFinalizersOnExit;
}
if (rfoe) runAllFinalizers();
}
// 退出操作。runHooks > runFinalizersOnExit > halt
static void exit(int status) {
boolean runMoreFinalizers = false;
synchronized (lock) {
if (status != 0) runFinalizersOnExit = false;
switch (state) {
case RUNNING:
state = HOOKS;
break;
case HOOKS:
break;
case FINALIZERS:
if (status != 0) {
halt(status);
} else {
runMoreFinalizers = runFinalizersOnExit;
}
break;
}
}
if (runMoreFinalizers) {
runAllFinalizers();
halt(status);
}
synchronized (Shutdown.class) {
sequence();
halt(status);
}
}
// shutdown操作,与exit不同的是,不做halt操作(关闭JVM)
static void shutdown() {
synchronized (lock) {
switch (state) {
case RUNNING:
state = HOOKS;
break;
case HOOKS:
case FINALIZERS:
break;
}
}
synchronized (Shutdown.class) {
sequence();
}
}
}
ApplicationShutdownHooks
它是对Shutdown的一个代理类。主要是添加、删除hook的适配。
class ApplicationShutdownHooks {
private static IdentityHashMap<Thread, Thread> hooks; // hook线程的存储器
// 初始化时,向Shutdown中添加hook.并初始化hooks容器。
static {
try {
Shutdown.add(1,
false,
new Runnable() {
public void run() {
runHooks();
}
}
);
hooks = new IdentityHashMap<>();
} catch (IllegalStateException e) {
// application shutdown hooks cannot be added if
// shutdown is in progress.
hooks = null;
}
}
// 添加hook
static synchronized void add(Thread hook) {
if(hooks == null)
throw new IllegalStateException("Shutdown in progress");
if (hook.isAlive())
throw new IllegalArgumentException("Hook already running");
if (hooks.containsKey(hook))
throw new IllegalArgumentException("Hook previously registered");
hooks.put(hook, hook);
}
// 删除hook
static synchronized boolean remove(Thread hook) {
if(hooks == null)
throw new IllegalStateException("Shutdown in progress");
if (hook == null)
throw new NullPointerException();
return hooks.remove(hook) != null;
}
// 执行hook
static void runHooks() {
Collection<Thread> threads;
synchronized(ApplicationShutdownHooks.class) {
threads = hooks.keySet();
hooks = null;
}
for (Thread hook : threads) {
hook.start();
}
for (Thread hook : threads) {
try {
hook.join();
} catch (InterruptedException x) { }
}
}
}
Runtime
运行时执行类。
public class Runtime {
private static Runtime currentRuntime = new Runtime(); // 静态的Runtime
// 静态方法获取Runtime.这个也是获取runtime的单例方法。
public static Runtime getRuntime() {
return currentRuntime;
}
// Shutdown.exit()退出的适配方法
public void exit(int status) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkExit(status);
}
Shutdown.exit(status);
}
// 添加hook
public void addShutdownHook(Thread hook) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("shutdownHooks"));
}
ApplicationShutdownHooks.add(hook);
}
// 删除hook
public boolean removeShutdownHook(Thread hook) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("shutdownHooks"));
}
return ApplicationShutdownHooks.remove(hook);
}
// 关闭程序
public void halt(int status) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkExit(status);
}
Shutdown.halt(status);
}
。。。
}
总结:
在Shutdown的执行顺序:
runHooks > runFinalizersOnExit > halt
Shutdown和ShutdownHooks不能被外部类直接调用,需要通过Runtime来代理操作。
应用实例
public class PollingServerListUpdater implements ServerListUpdater {
static ScheduledThreadPoolExecutor _serverListRefreshExecutor = null;
static {
_serverListRefreshExecutor = new ScheduledThreadPoolExecutor(coreSize, factory); // 定时任务线程池
_shutdownThread = new Thread(new Runnable() { // shutdown hook
public void run() {
logger.info("Shutting down the Executor Pool for PollingServerListUpdater");
shutdownExecutorPool();
}
});
Runtime.getRuntime().addShutdownHook(_shutdownThread); // 添加hook
}
// hook线程中的清理方法。
private static void shutdownExecutorPool() {
if (_serverListRefreshExecutor != null) {
_serverListRefreshExecutor.shutdown();
if (_shutdownThread != null) {
try {
Runtime.getRuntime().removeShutdownHook(_shutdownThread);
} catch (IllegalStateException ise) { // NOPMD
// this can happen if we're in the middle of a real
// shutdown,
// and that's 'ok'
}
}
}
}
}
转载于:https://blog.51cto.com/881206524/2147104