โ 1. Runtime.addShutdownHook()
Runtime.addShutdownHook()๋ ์๋ฐ ์ ํ๋ฆฌ์ผ์ด์
์ด ์ข
๋ฃ๋ ๋ ์คํํ ์ฝ๋๋ฅผ ๋ฑ๋กํ๋ ๋ฉ์๋์ด๋ค. ์ด๋ฅผ ํ์ฉํ๋ฉด ํ๋ก๊ทธ๋จ์ด ์ข
๋ฃ๋๊ธฐ ์ง์ ์ ํน์ ์์
(ํ์ผ ์ ์ฅ, ๋ฆฌ์์ค ์ ๋ฆฌ ๋ฑ)์ ์ํํ ์ ์๋ค.
โ 2. JVM ์ข ๋ฃ ์ด๋ฒคํธ๋?
์ฌ๊ธฐ์ ์ ์ ๋๋ ๋น์ ์ ์ข ๋ฃ์ ์ ์๋ ๋ช ์ธ์์ ๋์ ์๋ค. ์๋ฐ์ ๊ฐ์ ๋จธ์ ์ ๋ ๊ฐ์ง ์ข ๋ฅ์ ์ด๋ฒคํธ๋ฅผ ๋ฐ์ ์ข ๋ฃํ๋ค.
์ ์ ์ข
๋ฃ
- ํ๋ก๊ทธ๋จ์ด ์ ์์ ์ผ๋ก ์ข ๋ฃ๋ ๋
- System.exit()๊ฐ ํธ์ถ๋ ๋
๋น์ ์ ์ข ๋ฃ
- Ctrl + C(์ฌ์ฉ์ ์ธํฐ๋ฝํธ)
- ์ฌ์ฉ์ ๋ก๊ทธ์คํ ๋๋ ์์คํ ์ข ๋ฃ ๋ฑ ์์คํ ์ด๋ฒคํธ
โ ๏ธ ํ์ง๋ง ์๋์ ๊ฐ์ ๊ฒฝ์ฐ์๋ ์คํ๋์ง ์์ ์ ์๋ค.
- kill -9 (๊ฐ์ ์ข ๋ฃ)
- OutOfMemoryError ๋ฐ์
๐ ์ ๋ฆฌํ์๋ฉด
- ํ๋ก๊ทธ๋จ์ด ์ ์ ์ข ๋ฃ๋๊ฑฐ๋, Ctrl + C ๊ฐ์ ์ ํธ๋ก ์ข ๋ฃ๋ ๋ ์คํ๋จ
- System.exit()๊ฐ ํธ์ถ๋๊ฑฐ๋, JVM์ด ์ ์ ์ข ๋ฃ๋ ๋๋ ์คํ๋จ
- ํ์ง๋ง kill -9(๊ฐ์ ์ข ๋ฃ) ๋๋ OutOfMemoryError ๊ฐ์ ์์ธ ์ํฉ์์๋ ์คํ๋์ง ์์ ์๋ ์์
โ 3. ์ฝ๋ ํ๋ฆ
public class ShutdownHookExample {
public static void main(String[] args) {
// ์ข
๋ฃ ํ
๋ฑ๋ก
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("ํ๋ก๊ทธ๋จ์ด ์ข
๋ฃ๋๊ธฐ ์ ์ ์คํํ ์์
");
}));
System.out.println("ํ๋ก๊ทธ๋จ ์คํ ์ค...");
try {
Thread.sleep(5000); // 5์ด ๋์ ์คํ ์ ์ง
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ํ๋ก๊ทธ๋จ ์ข
๋ฃ");
}
}
๐ก ์คํ ๊ฒฐ๊ณผ (์ฝ์โผ)
ํ๋ก๊ทธ๋จ ์คํ ์ค...
ํ๋ก๊ทธ๋จ ์ข
๋ฃ
ํ๋ก๊ทธ๋จ์ด ์ข
๋ฃ๋๊ธฐ ์ ์ ์คํํ ์์
JVM์ด ์ข
๋ฃ๋๊ธฐ ์ง์ ์ ๋ฏธ๋ฆฌ ๋ฑ๋กํ ์ฝ๋(Shutdown Hook)๊ฐ ์ข
๋ฃ ํ ์คํ๋๋ ๊ฒ์ ์ฝ์์์ ํ์ธ ๊ฐ๋ฅํ๋ค.
โ 4. ์ธ์ ์จ?
- ํ์ผ ์ ์ฅ ๋๋ ๋ก๊ทธ ๊ธฐ๋ก: ํ๋ก๊ทธ๋จ์ด ์ข ๋ฃ๋๊ธฐ ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ๋
- ๋ฆฌ์์ค ์ ๋ฆฌ (์: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ํด์ ) :์คํ ์ค์ด๋ ํ๋ก์ธ์ค๋ฅผ ์ ๋ฆฌ ํ ๋ ์ ์ฉํจ
- ์๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ์์ ํด๋ฆฐ์ ์์ ์ํ: ์น ์๋ฒ๊ฐ ์ข ๋ฃ์ ํน์ ์ ๋ฆฌ ์์ ์ ์ํ ๊ฐ๋ฅ
โ ์ฌ์ฉ ์์
public class ShutdownHookTest {
// HookThread๋ Thread ํด๋์ค๋ฅผ ์์๋ฐ์ run() ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ ์ค๋ ๋์ด๋ค.
// ์ด ์ค๋ ๋๋ JVM ์ด ์ข
๋ฃ๋ ๋ ์คํํ๊ณ , ์คํ์ "Hook Run"์ ์ถ๋ ฅํ๋ค.
static class HookThread extends Thread {
@Override
public void run() {
System.out.println("Hook Run");
}
}
public static void main(String[] args) {
// JVM ์ข
๋ฃ ์ ์คํ๋ Shutdown Hook ๋ฑ๋ก
// Runtime.getRuntime()์ ํตํด Runtime ๊ฐ์ฒด ๊ฐ์ ธ์ค๊ธฐ
// addShutdownHook()์ ํธ์ถํ์ฌ HookThread๋ฅผ ๋ฑ๋กํ๋ค
Runtime.getRuntime().addShutdownHook(new HookThread());
System.out.println("End");
}
}
๋ฉ์ธ์์ "End"๋ฅผ ์ถ๋ ฅํ ํ ํ๋ก๊ทธ๋จ์ด ์ข
๋ฃ๋๋ค. JVM ์ข
๋ฃ์ Shutdown Hook์ด ์คํ๋๋ค.
๐ ์๋์ฒ๋ผ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด?
โEndโ๋ ์ถ๋ ฅ๋์ง ์์ง๋ง โHook Runโ์ด ์ถ๋ ฅ๋๋ค.
// ...
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new HookThread());
int errorNum = 1 / 0;
System.out.println("End");
}
}
Exception in thread "main" java.lang.ArithmeticException: / by zero
at ShutdownHookTest.main(ShutdownHookTest.java:10)
Hook Run
๐ ์ฝ์์์ sleep ๋์ ์ธํฐ๋ฝํธ(Ctrl+C)๋ฅผ ์ฃผ๋ ๊ฒฝ์ฐ
โEndโ๋ ์ถ๋ ฅ๋์ง ์์ง๋ง โhook Runโ์ด ์ถ๋ ฅ๋๋ค.
// ...
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new HookThread());
try {
System.out.println("sleep 3s");
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("End");
}
}
sleep 3s
End
Hook Run
๋ฉ์ธ์์ ์ผ์ด๋๋ ์ผ
- ๋จผ์ "sleep 3s" ์ถ๋ ฅ๋๋ค.
- Thread.sleep(3000); โ 3์ด ๋์ ๋๊ธฐ (ํ๋ก๊ทธ๋จ์ด ๋ฐ๋ก ์ข ๋ฃ๋์ง ์๋๋ก)
- ๋ง์ฝ Thread.sleep() ์ค ์ธํฐ๋ฝํธ๊ฐ ๋ฐ์ํ๋ฉด ์์ธ ์ฒ๋ฆฌ
- "End"๋ฅผ ์ถ๋ ฅํ ํ ํ๋ก๊ทธ๋จ์ด ์ข ๋ฃ๋จ
- JVM์ด ์ข ๋ฃ๋๋ฉด์ Shutdown Hook ์คํ๋จ โ Hook Run ์ถ๋ ฅ
โญ ์์๋ฅผ ์ง์ ํ ์ ์์โ
๋ค์ค ์ค๋ ๋ ์คํ์ฒ๋ผ ์ฌ๋ฌ ๊ฐ์ Hook์ด ์์ ๊ฒฝ์ฐ ์คํ ์์๋ ์ ํด์ ธ ์์ง ์๋ค. JVM์ Hook ์ฐ๋ ๋๋ฅผ ๋ฑ๋ก๋ ์์๋๋ก ์คํํ๋ค๊ณ ๋ณด์ฅํ์ง ์๊ธฐ ๋๋ฌธ์, ๋ ์ฐ๋ ๋๊ฐ ์ด๋ค ์์๋ก ์คํ๋ ์ง๋ JVM์ ๋ฐ๋ผ ๋ฌ๋ผ์ง ์ ์๋ค.
public class ShutdownHookTest {
static class HookThread extends Thread {
@Override
public void run() {
System.out.println("Hook Run1");
}
}
static class HookThread2 extends Thread {
@Override
public void run() {
System.out.println("Hook Run2");
}
}
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new HookThread());
Runtime.getRuntime().addShutdownHook(new HookThread2());
System.out.println("End");
}
}
์ฒซ ๋ฒ์งธ ์คํ ๊ฒฐ๊ณผ
End
Hook Run1
Hook Run2
๋๋ฒ์งธ ์คํ ๊ฒฐ๊ณผ
End
Hook Run2
Hook Run1
โญ ๋ฑ๋ก๋ Shutdown Hook ์คํ ์ฐจ๋จ ๐ ๐ปโโ๏ธ
์ฌ์ฉํ ์ผ์ด ๋ณ๋ก ์์ ๊ฒ ๊ฐ์ง๋ง, Runtime.halt(int)๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฑ๋ก๋ Shutdown Hook์ ์คํํ์ง ์๊ณ ์ข
๋ฃ๋๋ค.
๋ง์ฝ ์ข
๋ฃ ์ค์ Halt๊ฐ ํธ์ถ๋ ๊ฒฝ์ฐ (์ข
๋ฃ ์ค์ด๋ผ๋ฉด ๋ฑ๋ก๋ Shutdown Hook ์ฐ๋ ๋๋ค์ด ๋์์ ์คํ๋๋ ์ํ) ์คํ ์ค์ธ Hook์ด ๋ง์น ๋๊น์ง ๋๊ธฐํ์ง ์๊ณ , ์ข
๋ฃํ๋ค.
์๋ ์์ ๋ฅผ ๋ณด๋ฉด ์ข
๋ฃ๋๊ธฐ ์ ์ halt() ๋ฉ์๋๋ฅผ ํธ์ถํ์์ผ๋ฏ๋ก ๋ฑ๋ก๋ Hook์ด ์คํ๋์ง ์๋๋ค.
public class ShutdownHookTest {
static class HookThread extends Thread {
@Override
public void run() {
System.out.println("Hook Run1");
}
}
static class HookThread2 extends Thread {
@Override
public void run() {
System.out.println("Hook Run2");
}
}
public static void main(String[] args) {
// Shutdown Hook ๋ฑ๋ก
Runtime.getRuntime().addShutdownHook(new HookThread());
Runtime.getRuntime().addShutdownHook(new HookThread2());
System.out.println("End");
// JVM์ ๊ฐ์ ๋ก ์ข
๋ฃ
Runtime.getRuntime().halt(0);
}
}
๐ฅhalt(0)์ ์ญํ
Runtime.getRuntime().halt(0)์ JVM์ ๊ฐ์ ๋ก ์ข ๋ฃ์ํค๋ ๋ฉ์๋์ด๋ค. halt() ๋ฉ์๋๋ ์ ์์ ์ธ ์ข ๋ฃ ์ ์ฐจ๋ฅผ ๋ฐ๋ฅด์ง ์๊ธฐ ๋๋ฌธ์, Shutdown Hook์ด ์คํ๋์ง ์๋๋ค.
- halt()๋ JVM์ด ์ข ๋ฃ๋๋ ์ฆ์ ๋ชจ๋ ํ์ ์์ ์ ์ค๋จํ๊ณ ์ข ๋ฃํ๋ค.
- ๋ฐ๋ผ์, halt()๋ฅผ ํธ์ถํ๋ฉด ๋ฑ๋ก๋ Shutdown Hook์ ์คํ๋์ง ์๋๋ค.
๐ halt(0) ํธ์ถ ์ ํ์ ์ฐจ์ด
์ ์์ ์ธ ์ข ๋ฃ (System.exit() ๋ฑ)์์๋ JVM์ด ์ข ๋ฃ๋๊ธฐ ์ ์ Shutdown Hook์ด ์คํ๋๋ค.
End
Hook Run1
Hook Run2
ํ์ง๋ง halt(0) ํธ์ถ ์, halt(0)์ ์ฆ์ JVM์ ์ข
๋ฃ์ํค๊ธฐ ๋๋ฌธ์, Shutdown Hook์ ์คํ๋์ง ์๋๋ค.
End
โญ Shutdown Hook์ด ์คํ๋๋ฉด, Hook์ ์ถ๊ฐ, ์ญ์ ํ ์ ์์โ
Shutdown Hook์ ๋์์ ๋ํ ์ ์ฝ์ด ์๋๋ฐ, Shutdown Hook์ด ์คํ๋๊ธฐ ์์ํ๋ฉด ๊ทธ ์์ ์ ๋ ์ด์ ์๋ก์ด Shutdown Hook์ ์ถ๊ฐํ๊ฑฐ๋ ๊ธฐ์กด์ Shutdown Hook์ ์ญ์ ํ ์ ์๋ค.
์ญ์ ๋ฉ์๋ Runtime.removeShutdownHook()
๋ง์ฝ ์๋ํ๋ค๋ฉด? IllegalStateException ์์ธ๊ฐ ๋ฐ์ํ๋ค.
์๋ ์์ ๋ ์์ธ๊ฐ ๋ฐ์ํ๋ ๊ฒฝ์ฐ์ด๋ค. Shutdown Hook์ ์ถ๊ฐํ Hook Thread์ ์๋ก์ด Hook์ ์ถ๊ฐํ์๋ค. ๋ฐ๋ผ์ ์ข
๋ฃ๋๋ฉด์ IllegalStateException ์์ธ๊ฐ ๋ฐ์ํ๋ค.
public class ShutdownHookTest {
static class HookThread extends Thread {
@Override
public void run() {
Runtime.getRuntime().addShutdownHook(new HookThread());
System.out.println("Hook Run1");
}
}
// ...
}
โ ๏ธ Runtime.addShutdownHook() ์ฌ์ฉ ์ ์ ์์ฌํญ
โ๏ธ ์ค๋ ๋๋ ์ด๋ ต๋ค
- Shutdown Hook ์ ๋ณ๋์ ์ค๋ ๋์์ ์คํ๋๋ฏ๋ก, ์ํ๋ ๋๋ก ๋์ํ์ง ์์ ์ ์์
- ์ค๋ ๋ ๋์์ ์์ํ๊ธฐ ์ด๋ ต๊ณ , ๋๋ฒ๊น
์ด ๊น๋ค๋ก์ธ ์ ์์
โ๏ธ ๋์์ ์คํ๋๋ฏ๋ก thread-safe ํ๊ฒ ์์ฑํ ๊ฒ!
- ํ๋ก๊ทธ๋จ์ด ์ข
๋ฃ๋ ๋, ๋ฑ๋ก๋ ๋ชจ๋ Shutdown Hook ์ฐ๋ ๋๊ฐ ๋์์ ์คํ๋จ
- ๋ค๋ฅธ ์ฐ๋ ๋๋ค๋ ์คํ ์ค์ผ ๊ฐ๋ฅ์ฑ์ด ์์ผ๋ฏ๋ก, ๋ฐ๋๋ฝ(Deadlock) ๋ฐ์ ๊ฐ๋ฅ์ฑ์ ๊ณ ๋ คํด์ผ ํจ
- ๊ณต์ ์์(ํ์ผ, DB ๋ฑ)์ ์ ๊ทผํ ๋๋ ๋๊ธฐํ(synchronization)๋ฅผ ์ ๊ฒฝ ์จ์ผ ํจ
โ๏ธ ๋ฌด์กฐ๊ฑด ์คํ๋๋ค๋ ๋ณด์ฅ์ด ์์
- `kill -9`(๊ฐ์ ์ข
๋ฃ) ๋๋ `OutOfMemoryError` ๋ฐ์ ์ ์คํ๋์ง ์์ ์๋ ์์
- ๋ฐ๋ผ์ Shutdown Hook์ 100% ์ ๋ขฐํ๋ฉด ์ ๋จ!
- ์ค์ํ ์์
์ ๋ณ๋๋ก ์ข
๋ฃ ๋ฉ์๋(cleanup method)๋ฅผ ๊ตฌํํ๋ ๊ฒ์ด ์์ ํจ
โ๏ธ Hook ์ฐ๋ ๋์์ ๋๋ฌด ๋ง์ ์๊ฐ์ ์๋ชจํ๋ฉด ์ ๋จ
- ์์คํ
์ข
๋ฃ ์ค์ `Shutdown Hook`์ด ์คํ๋๋๋ฐ, ๋๋ฌด ์ค๋ ๊ฑธ๋ฆฌ๋ฉด ์์คํ
์ด ๋ฌด์ํ๊ณ ๊ฐ์ ์ข
๋ฃํ ์ ์์
- ๋ฐ๋ผ์, ์ต์ํ์ ์์
๋ง ์ํํ๋ ๊ฒ์ด ๋ฐ๋์งํ๋ค