JAVA 핵심 지식 포인트-Shutdown Hook 을 이용 하여 시스템 자원 방출
9403 단어 JAVA 핵심 지식 포인트
Shutdown Hook 실행 원리
ShutdownHook 적용 필드
참고서:《자바 특전 사(상권)》
System.exit(int status)가 발생 했 을 때 시스템 이 종료 되 기 전에 약간의 임 무 를 수행 하여 자원 방면 의 회수 작업 을 하고 싶 습 니 다.Shutdown Hook 은 이 목적 을 달성 할 수 있 습 니 다.이것 은 hook 의 사고방식 을 이용 하여 실현 하고 어떤 때 는 이 를'갈고리'라 고도 부 릅 니 다.
시스템 에서 Runtime.getRuntime().exec(String command)나 new ProcessBuilder(List 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()를 통 해 만 든 스 레 드 대상 입 니 다.자바 프로 세 스 가 exit()를 호출 할 때 이 스 레 드 대상 의 start()방법 으로 실행 되 므 로 손 으로 먼저 시작 하지 마 십시오.또한,이러한 리 셋 스 레 드 는 고정 순환 프로그램 으로 설정 하지 마 십시오.그렇지 않 으 면 종료 할 수 없습니다.
Shutdown Hook 실행 원리
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.ApplicationShutdown Hooks 의 static 익명 블록 에 add()방법 으로 hook 를 등록 하 였 습 니 다.run()방법 내부 에서 내 부 를 실행 하 는 정적 방법 runHooks()는 내부 에 Identity HashMap 으로 hook 정 보 를 저장 하고 add()방법 으로 추가 할 수 있 으 며 프로그램 에서 정의 하 는 리 셋 스 레 드 를 여기에 두 었 습 니 다.그 자체 가 hook 형식 으로 hooks 목록 에 존재 합 니 다.
package java.lang;
import java.util.*;
/*
* Class to track and run user level shutdown hooks registered through
* {@link Runtime#addShutdownHook Runtime.addShutdownHook}.
*
* @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 hooks = new IdentityHashMap();
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 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)를 간접 적 으로 호출 하여 Identity HashMap 에 스 레 드 를 넣 고 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 대상 은 응용 프로그램 Shutdown Hooks 의 익명 블록 에 의 해 정의 되 기 때문에 응용 프로그램 Shutdown Hooks 류 의 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 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 적용 필드