JAVA核心知识点--利用ShutdownHook释放系统资源

目录

ShutdownHook执行原理

ShutdownHook适用场景


参考书籍:《Java特种兵(上册)》 

当发生 System.exit(int status) 时,希望在系统退出前,执行一点任务来做一些资源方面的回收操作,ShutdownHook 可以达到这个目的,它利用 hook 的思路来实现,有些时候也把它叫作“钩子”。

假如在系统中通过 Runtime.getRuntime().exec(String command) 或 new ProcessBuilder(List<String> command) 启动了子进程(Process),这个子进程一直在运行中,在当前进程退出时,子进程未必会退出,但此时业务上希望将它退出,就可以利用 ShutdownHook 。例如下面这个测试样例: 

public class ShutdownHookTest {

	public static void main(String[] args) {
		Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
			public void run() {
				System.out.println("执行ShutdownHook钩子操作,释放系统资源...");
			}
		}));
		Runtime.getRuntime().removeShutdownHook(new Thread());
		System.exit(1);
		System.out.println("主进程结束...");
	}
}

执行结果:

主进程结束...
执行ShutdownHook钩子操作,释放系统资源...

注意:传入参数是通过 new Thread() 创建的线程对象,在Java进程调用 exit() 时,会调用该线程对象的 start()方法将其运行起来,所以不要手工先启动了。另外,这种回调线程就不要设定为死循环程序,否则就无法退出了。

ShutdownHook执行原理

java.lang包下有一个Shutdown 类,提供了一个 Runnable[] hooks 数组,数组的长度为10,也就是最多定义10个hook(这与程序写入多少个线程回调没有关系),提供 add() 方法来注册新的 hook 对象。 

    // The system shutdown hooks are registered with a predefined slot.
    // The list of shutdown hooks is as follows:
    // (0) Console restore hook
    // (1) Application hooks
    // (2) DeleteOnExit hook
    private static final int MAX_SYSTEM_HOOKS = 10;
    private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS];
    /* Add a new shutdown hook.  Checks the shutdown state and the hook itself,
     * but does not do any security checks.
     */
    static void add(int slot, Runnable hook) {
        synchronized (lock) {
            if (state > RUNNING)
                throw new IllegalStateException("Shutdown in progress");

            if (hooks[slot] != null)
                throw new InternalError("Shutdown hook at slot " + slot + " already registered");

            hooks[slot] = hook;
        }
    }
    

在java.lang.ApplicationShutdownHooks 的static 匿名块中,通过 add() 方法注册了一个hook ,在 run() 方法内部运行内部的静态方法 runHooks() ,它内部用一个 IdentityHashMap 来存放 hook 信息,可以通过 add() 方法来添加,而程序中定义的回调线程都放在了这里,它自身用hook 的形式存在于 hooks 列表当中。 

package java.lang;

import java.util.*;

/*
 * Class to track and run user level shutdown hooks registered through
 * <tt>{@link Runtime#addShutdownHook Runtime.addShutdownHook}</tt>.
 *
 * @see java.lang.Runtime#addShutdownHook
 * @see java.lang.Runtime#removeShutdownHook
 */

class ApplicationShutdownHooks {
    static {
        Shutdown.add(1 /* shutdown hook invocation order */,
            new Runnable() {
                public void run() {
                    runHooks();
                }
            });
    }

    /* The set of registered hooks */
    private static IdentityHashMap<Thread, Thread> hooks = new IdentityHashMap<Thread, Thread>();

    private void ApplicationShutdownHooks() {}

    /* Add a new shutdown hook.  Checks the shutdown state and the hook itself,
     * but does not do any security checks.
     */
    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);
    }

    /* Remove a previously-registered hook.  Like the add method, this method
     * does not do any security checks.
     */
    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;
    }

    /* Iterates over all application hooks creating a new thread for each
     * to run in. Hooks are run concurrently and this method waits for 
     * them to finish.
     */
    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.getRuntime().addShutdownHook(Thread hook)方法时,会间接调用 ApplicationShutdownHooks.add(Thread hook)将线程放到IdentityHashMap 中,Runtime.getRuntime().removeShutdownHook(Thread)用于删除一个钩子线程。 

public void addShutdownHook(Thread hook) {
	SecurityManager sm = System.getSecurityManager();
	if (sm != null) {
	    sm.checkPermission(new RuntimePermission("shutdownHooks"));
	}
	ApplicationShutdownHooks.add(hook);
    }
public boolean removeShutdownHook(Thread hook) {
	SecurityManager sm = System.getSecurityManager();
	if (sm != null) {
	    sm.checkPermission(new RuntimePermission("shutdownHooks"));
	}
	return ApplicationShutdownHooks.remove(hook);
    }

当调用 System.exit(int status)方法时,会间接调用 Shutdown.exit(int status)方法,再调用sequence()-->runHooks() 方法。 

    /* Invoked by Runtime.exit, which does all the security checks.
     * Also invoked by handlers for system-provided termination events,
     * which should pass a nonzero status code.
     */
    static void exit(int status) {
	boolean runMoreFinalizers = false;
	synchronized (lock) {
	    if (status != 0) runFinalizersOnExit = false;
	    switch (state) {
	    case RUNNING:	/* Initiate shutdown */
		state = HOOKS;
		break;
	    case HOOKS:		/* Stall and halt */
		break;
	    case FINALIZERS:
		if (status != 0) {
		    /* Halt immediately on nonzero status */
		    halt(status);
		} else {
		    /* Compatibility with old behavior:
		     * Run more finalizers and then halt
		     */
		    runMoreFinalizers = runFinalizersOnExit;
		}
		break;
	    }
	}
	if (runMoreFinalizers) {
	    runAllFinalizers();
	    halt(status);
	}
	synchronized (Shutdown.class) {
	    /* Synchronize on the class object, causing any other thread
             * that attempts to initiate shutdown to stall indefinitely
	     */
	    sequence();
	    halt(status);
	}
    }
    /* The actual shutdown sequence is defined here.
     *
     * If it weren't for runFinalizersOnExit, this would be simple -- we'd just
     * run the hooks and then halt.  Instead we need to keep track of whether
     * we're running hooks or finalizers.  In the latter case a finalizer could
     * invoke exit(1) to cause immediate termination, while in the former case
     * any further invocations of exit(n), for any n, simply stall.  Note that
     * if on-exit finalizers are enabled they're run iff the shutdown is
     * initiated by an exit(0); they're never run on exit(n) for n != 0 or in
     * response to SIGINT, SIGTERM, etc.
     */
    private static void sequence() {
	synchronized (lock) {
	    /* Guard against the possibility of a daemon thread invoking exit
	     * after DestroyJavaVM initiates the shutdown sequence
	     */
	    if (state != HOOKS) return;
	}
	runHooks();
	boolean rfoe;
	synchronized (lock) {
	    state = FINALIZERS;
	    rfoe = runFinalizersOnExit;
	}
	if (rfoe) runAllFinalizers();
    }
    /* 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 {
		if (hook != null) hook.run();
	    } catch(Throwable t) { 
		if (t instanceof ThreadDeath) {
   		    ThreadDeath td = (ThreadDeath)t;
		    throw td;
		} 
	    }
	}
    }

循环中的一个hook对象就是由 ApplicationShutdownHooks 的匿名块定义的,因此会调用 ApplicationShutdownHooks 类的 run() 方法,再调用它的runHooks()方法,这个 runHooks()方法内容如下。 

/* Iterates over all application hooks creating a new thread for each
     * to run in. Hooks are run concurrently and this method waits for 
     * them to finish.
     */
    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) { }
	}
    }

从这个方法中取出所有的Thread ,然后将线程启动起来,最后通过 join() 方法等待各个线程结束的动作,换句话说,在进程关闭前,对多个回调任务的处理方式是每个任务单独有一个线程处理,而不是所有的任务在一个线程中串行处理。  

ShutdownHook适用场景

  1. 程序正常退出
  2. 使用System.exit()
  3. 终端使用Ctrl+C触发的中断
  4. 系统关闭
  5. OutOfMemory宕机
  6. 使用Kill pid命令干掉进程(注:在使用kill -9 pid时,是不会被调用的)
可将 TTL/UART 串口设备连接至以太网 支持网口升级程序 支持TCP服务器、TCP客户端、UDP模式 支持虚拟串口、Web登录或使用VirCom进行配置 ZLSN2003 概述 ZLSN2003是上海卓岚开发的新一代串口以太网嵌入式模块。该模块功能强大,其基本功能是实现串口联网的方便性,即只要和用户的串口TTL电平的串口连接,ZLSN2003就可以将数据发送到基于TCP/IP/UDP的网络服务器上。默认情况下串口和以太网之间是透明传输协议,即串口发送什么,网络就收到什么数据,不会数据格式化。ZLSN2003内部已经集成网络变压器,外围电路非常简单。 ZLSN2003模块为单片机联网、传统串口设备联网提供了快捷、稳定、经济的方案。不仅能够保证您的产品快速上市,并且为您的产品的稳定提供支撑。 特点 支持在线网络升级固件程序,用户可以从卓岚公司获得软件升级工具和升级firmware,可自行升级到最高版本。 使用配置的ZLVircom工具可以搜索、管理局域网内(支持跨网段搜索)、Internet上的ZLSN2003模块。可一键式配置模块的所有参数。设备配置、管理非常方便。 支持DHCP功能,可以动态获得局域网内的DHCP服务器分配的IP。 支持DNS,自动解析目的域名为IP,目的IP可以为动态域名。 作为TCP Server(TCP服务器端)时,支持独有的100个连接的强大连接能力。 作为TCP Client(TCP客户端)的,支持连接8个目标服务器。作为TCP客户端时,可以在断线后自动进行重连。支持隐含心跳技术,保证网线断线后的恢复。 支持UDP、UDP组播等功能。 支持虚拟串口。 规格 网络界面 以太网 10/100 Mbps 保护 内建1KV电磁隔离 串口界面 界面 TTL电平串口 串口数 1 校验位 None, Even, Odd, Space, Mark 数据位 5~9 停止位 1,2 流控 RTS/CTS,DTR/DCR,XON/XOFF 速率 1200bps~460800bps 软件特性 协议 TCP,HTTP,UDP,ICMP,ARP,IP,DNS,DHCP 虚拟串口平台 Windows 95/98/ME/NT/2000/XP/WIN7/WIN8 配置方式 Web浏览器、卓岚ZLVirCom、串口类AT命令 电器特性 电压输入 DC5V(ZLSN2003-3.3V支持3.3V),210~250mA 机械特性 尺寸 长×宽=43 × 26mm 工作环境 工作温度,湿度 0~70℃,5~95% RH 储存温度,湿度 -45~165℃,5~95% RH W
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值