F-LAB JAVA · 4주차 · Phase 3 · 스레드 만들고 다루기
이 Unit을 끝내면 다음을 답할 수 있어야 한다.
데몬 스레드 (Daemon Thread) 는 일반 스레드 (User Thread) 를 보조하는 백그라운드 스레드로, 모든 일반 스레드가 종료되면 JVM 과 함께 자동으로 종료된다.
JVM 은 모든 일반 (non-daemon) 스레드가 종료되면 종료 되며, 이때 남아있는 데몬 스레드는 작업 완료 여부와 무관하게 강제 종료된다.
데몬 설정은setDaemon(true)로 start() 호출 전에만 가능하다 (이후 호출 시 IllegalThreadStateException).
용도는 GC, 모니터링, 로깅 등 메인 작업을 보조하는 백그라운드 작업이며, 작업 완료를 보장해야 하는 작업 (파일 저장, DB 커밋) 은 데몬으로 설정하면 안 된다 — JVM 종료 시 중간에 강제 종료되어 데이터 손실이 발생할 수 있다.
메인 스레드가 종료되어도 다른 일반 스레드가 살아있으면 데몬은 계속 실행 된다 (메인이 아니라 "모든 일반 스레드" 가 기준).
일반 스레드 (User Thread) = 정직원:
- 매장의 핵심 업무
- 정직원이 모두 퇴근해야 매장 마감
데몬 스레드 (Daemon Thread) = 보조 알바:
- 청소, 음악 틀기 등 보조
- 정직원이 모두 퇴근하면
- 알바도 (하던 일 중이라도) 함께 퇴근
매장 마감 (JVM 종료):
- 모든 정직원 퇴근 시
- 알바는 작업 중이라도 강제 퇴근
위험:
- 중요 업무 (금고 정산) 를 알바에게 맡기면?
- 정직원 퇴근 시 정산 중간에 알바도 퇴근
- 정산 미완료 (데이터 손실)
→ 중요 작업은 정직원 (일반 스레드)
→ 데몬 = 보조 알바, 일반 = 정직원, 모든 정직원 퇴근 시 알바도 강제 퇴근.
1. 데몬 스레드의 정의
2. 일반 스레드 vs 데몬 스레드
3. JVM 종료 조건
4. setDaemon(true) 설정
5. 데몬 스레드의 용도
6. 메인 스레드 종료와 데몬
7. 작업 완료 보장과 데몬 위험
8. 데몬의 자식 스레드와 실무
9. 면접 + 자기 점검
데몬 스레드 (Daemon Thread):
일반 스레드를 보조하는 백그라운드 스레드.
모든 일반 스레드 종료 시 JVM 과 함께 종료.
특징:
- 보조 작업
- 일반 스레드에 의존
- 자동 종료
"Daemon" 의 의미:
- 그리스 신화의 정령 (보조 존재)
- Unix 의 백그라운드 프로세스 (httpd, sshd)
- 보이지 않게 일하는 보조
자바:
- 일반 스레드 뒤에서 보조
- GC 스레드가 대표적
Thread daemon = new Thread(() -> {
while (true) {
doBackgroundWork();
Thread.sleep(1000);
}
});
daemon.setDaemon(true); // ★ 데몬으로 설정 (start 전)
daemon.start();
// 일반 스레드 종료 시
// 이 데몬도 (무한 루프 중이라도) 종료
Thread t = new Thread(task);
System.out.println(t.isDaemon()); // false (기본)
t.setDaemon(true);
System.out.println(t.isDaemon()); // true
// 메인 스레드는 일반 스레드
System.out.println(Thread.currentThread().isDaemon()); // false
데몬 스레드의 기본값:
새 스레드의 데몬 여부 = 생성한 스레드의 데몬 여부 상속
메인 스레드 (일반) 가 만든 스레드:
→ 일반 (false)
데몬 스레드가 만든 스레드:
→ 데몬 (true)
public class ShipmentDaemonExample {
// 백그라운드 모니터링 (데몬)
public void startMonitoring() {
Thread monitor = new Thread(() -> {
while (true) {
logSystemStats();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
break;
}
}
});
monitor.setDaemon(true); // 데몬 (보조 작업)
monitor.start();
// 메인 작업 종료 시 모니터링도 자동 종료
}
private void logSystemStats() {
log.info("Memory: {}, Threads: {}",
Runtime.getRuntime().freeMemory(),
Thread.activeCount());
}
}
데몬 스레드의 정의는?
답:
1. 정의:
어원:
설정:
기본값:
| 항목 | 일반 스레드 (User) | 데몬 스레드 (Daemon) |
|---|---|---|
| 역할 | 핵심 작업 | 보조 작업 |
| JVM 종료 | 모두 종료해야 JVM 종료 | JVM 종료 시 강제 종료 |
| 기본값 | false (일반) | (상속) |
| 예시 | 메인, 작업 스레드 | GC, 모니터링 |
| 작업 보장 | 완료 보장 | 보장 X |
일반 스레드 (User Thread):
- 핵심 작업 수행
- JVM 은 모든 일반 스레드 종료까지 살아있음
- 작업 완료 보장
예:
- main 스레드
- 작업 처리 스레드
- 명시적으로 만든 스레드 (기본)
데몬 스레드 (Daemon Thread):
- 보조 작업
- 일반 스레드 종료 시 강제 종료
- 작업 완료 보장 X
예:
- GC 스레드
- JIT 컴파일러 스레드
- 모니터링, 로깅
- finalize 스레드
일반 스레드 vs 데몬:
일반 스레드들:
main: [████████████]
worker: [████████████████]
↑ 모두 종료
데몬:
monitor: [████████░░░░] ← 강제 종료 (작업 중이라도)
↑
일반 스레드 종료 시점에 끊김
JVM 종료:
- 모든 일반 스레드 종료 시
- 데몬 강제 종료
public class UserVsDaemon {
public static void main(String[] args) throws InterruptedException {
// 일반 스레드
Thread userThread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("User: " + i);
sleep(500);
}
});
userThread.start();
// 데몬 스레드
Thread daemonThread = new Thread(() -> {
while (true) { // 무한 루프
System.out.println("Daemon working");
sleep(500);
}
});
daemonThread.setDaemon(true);
daemonThread.start();
// main 종료 시:
// - userThread 완료까지 JVM 유지
// - userThread 종료 후 daemonThread 강제 종료
// (무한 루프지만 종료됨)
}
static void sleep(long ms) {
try { Thread.sleep(ms); } catch (Exception e) {}
}
}
public class ShipmentThreadTypes {
// 일반 스레드 — 핵심 처리 (완료 보장)
public void processCore(Shipment shipment) {
Thread worker = new Thread(() -> {
repository.save(shipment); // 중요! 완료 보장 필요
});
// 데몬 설정 X (기본 일반)
worker.start();
}
// 데몬 스레드 — 보조 (모니터링)
public void startHealthMonitor() {
Thread monitor = new Thread(() -> {
while (true) {
checkHealth();
sleep(10000);
}
});
monitor.setDaemon(true); // 데몬 (보조)
monitor.start();
}
private void sleep(long ms) {
try { Thread.sleep(ms); } catch (Exception e) {}
}
}
일반 스레드 vs 데몬 스레드는?
답:
1. 일반 스레드:
데몬 스레드:
JVM 종료:
예시:
JVM 종료 조건:
모든 일반 (non-daemon) 스레드가 종료되면
JVM 종료.
이때:
- 데몬 스레드는 강제 종료
- 작업 완료 무관
핵심:
- 일반 스레드 = JVM 생명줄
- 데몬은 무관
JVM 종료 규칙:
1. 모든 일반 스레드 종료
→ JVM 종료 시작
2. 데몬 스레드 강제 종료
→ 작업 중이라도
3. System.exit() 호출
→ 즉시 종료 (모든 스레드)
핵심:
- "일반 스레드 0개" 가 조건
- 데몬 수는 무관
JVM 종료 시점:
시간 →
일반1: [████████]
일반2: [████████████]
↑ 마지막 일반 스레드 종료
데몬1: [████░░░░░░░░] ← 여기서 강제 종료
데몬2: [██████░░░░░░] ← 여기서 강제 종료
↑ JVM 종료
일반 스레드 모두 종료 → JVM 종료 → 데몬 강제 종료
public static void main(String[] args) {
// 데몬 스레드 (무한)
Thread daemon = new Thread(() -> {
while (true) {
System.out.println("daemon");
sleep(100);
}
});
daemon.setDaemon(true);
daemon.start();
// main (일반) 이 끝나면
System.out.println("Main ending");
// main 종료
// → 일반 스레드 0개 (main 만 있었음)
// → JVM 종료
// → daemon 강제 종료 (무한 루프지만 끝남)
}
public static void main(String[] args) {
// 데몬
Thread daemon = new Thread(() -> infiniteWork());
daemon.setDaemon(true);
daemon.start();
// 다른 일반 스레드
Thread user = new Thread(() -> {
sleep(10000); // 10초
});
user.start(); // 일반 (데몬 설정 X)
// main 종료
// → 하지만 user (일반) 살아있음
// → JVM 유지
// → daemon 계속 (user 가 살아있으니)
// user 종료 (10초 후)
// → 일반 스레드 0개
// → JVM 종료
// → daemon 강제 종료
}
public class JvmShutdownExample {
public static void main(String[] args) {
// 데몬 — 백그라운드 모니터링
Thread monitor = new Thread(() -> {
while (true) {
logStats();
sleep(5000);
}
});
monitor.setDaemon(true);
monitor.start();
// 일반 — 핵심 처리
Thread processor = new Thread(() -> {
processAllShipments(); // 완료까지 JVM 유지
});
processor.start();
// main 종료해도
// processor (일반) 살아있으면 JVM 유지
// monitor (데몬) 도 계속
// processor 종료 시
// → JVM 종료
// → monitor 강제 종료
}
static void logStats() { }
static void processAllShipments() { }
static void sleep(long ms) { try { Thread.sleep(ms); } catch (Exception e) {} }
}
JVM 종료 조건은?
답:
1. 조건:
데몬:
규칙:
System.exit():
Thread t = new Thread(task);
// ✓ start() 전에 설정
t.setDaemon(true);
t.start();
// ❌ start() 후 설정
t.start();
t.setDaemon(true); // IllegalThreadStateException
start() 전에만 가능한 이유:
데몬 여부는 스레드 시작 시 결정.
- 시작 후 변경은 의미 모호
- 이미 실행 중인데 종류 변경?
→ start 전에 확정
→ 후에는 예외
Thread t = new Thread(task);
t.start(); // RUNNABLE
t.setDaemon(true); // ❌ IllegalThreadStateException
// "이미 시작된 스레드는 데몬 변경 불가"
// 올바른 순서
Thread t2 = new Thread(task);
t2.setDaemon(true); // 먼저
t2.start(); // 그 다음
// 설정
void setDaemon(boolean on);
// 확인
boolean isDaemon();
// 사용
Thread t = new Thread(task);
t.setDaemon(true);
System.out.println(t.isDaemon()); // true
t.start();
// ThreadFactory 로 데몬 설정
ThreadFactory daemonFactory = r -> {
Thread t = new Thread(r);
t.setDaemon(true); // 팩토리에서 설정
return t;
};
// Executor 에 적용
ExecutorService executor = Executors.newFixedThreadPool(4, daemonFactory);
// 풀의 모든 스레드가 데몬
// 람다로 간결
ThreadFactory factory = r -> {
Thread t = new Thread(r, "daemon-worker");
t.setDaemon(true);
return t;
};
public class DaemonThreadFactory {
// 데몬 스레드 팩토리
public static ThreadFactory daemonFactory(String prefix) {
AtomicInteger counter = new AtomicInteger();
return r -> {
Thread t = new Thread(r, prefix + "-" + counter.incrementAndGet());
t.setDaemon(true); // 데몬
return t;
};
}
// 모니터링 풀 (데몬)
public ScheduledExecutorService createMonitorPool() {
return Executors.newScheduledThreadPool(
2,
daemonFactory("monitor"));
// 모니터링 스레드는 데몬 (앱 종료 시 자동 종료)
}
// 핵심 작업 풀 (일반)
public ExecutorService createWorkerPool() {
return Executors.newFixedThreadPool(4);
// 기본 일반 스레드 (작업 완료 보장)
}
}
setDaemon(true) 설정은?
답:
1. 시점:
이유:
예외:
활용:
데몬 스레드 적합 용도:
1. GC (Garbage Collection)
- JVM 의 GC 스레드
- 백그라운드 메모리 정리
2. 모니터링
- 시스템 상태 체크
- 메트릭 수집
3. 로깅
- 비동기 로그 기록
- 백그라운드 flush
4. 캐시 정리
- 만료 항목 제거
- 주기적 정리
5. 하트비트
- 연결 유지
- 상태 확인
데몬에 적합한 작업의 특징:
1. 보조적
- 핵심 비즈니스 X
- 지원 역할
2. 무한/반복
- 계속 실행
- 명시적 종료 불필요
3. 완료 보장 불필요
- 중간 종료 OK
- 데이터 손실 무관
4. 앱과 생명 같이
- 앱 종료 시 함께
// JVM 의 GC 스레드는 데몬
// 직접 만들지 않지만 개념 이해
// 모든 GC, JIT 스레드:
// - 데몬
// - 백그라운드
// - 앱 종료 시 함께
// 확인 (대략)
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
// GC 관련 스레드는 데몬
public class SystemMonitor {
public void start() {
Thread monitor = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
logMemory();
logThreadCount();
sleep(10000);
}
}, "system-monitor");
monitor.setDaemon(true); // 데몬 (보조)
monitor.start();
// 앱 종료 시 모니터링도 자동 종료
// 명시적 종료 불필요
}
private void logMemory() {
Runtime rt = Runtime.getRuntime();
log.info("Memory: {}/{}",
rt.totalMemory() - rt.freeMemory(),
rt.maxMemory());
}
private void logThreadCount() {
log.info("Threads: {}", Thread.activeCount());
}
private void sleep(long ms) {
try { Thread.sleep(ms); } catch (Exception e) {
Thread.currentThread().interrupt();
}
}
}
public class CacheCleaner {
private final Map<String, CacheEntry> cache = new ConcurrentHashMap<>();
public void startCleaner() {
Thread cleaner = new Thread(() -> {
while (true) {
removeExpired();
sleep(60000); // 1분마다
}
}, "cache-cleaner");
cleaner.setDaemon(true); // 데몬 (보조)
cleaner.start();
}
private void removeExpired() {
long now = System.currentTimeMillis();
cache.entrySet().removeIf(e -> e.getValue().expiry < now);
}
record CacheEntry(Object value, long expiry) {}
private void sleep(long ms) { try { Thread.sleep(ms); } catch (Exception e) {} }
}
@Component
public class ShipmentBackgroundTasks {
// 1. 캐시 정리 (데몬)
public void startCacheCleaner() {
Thread cleaner = new Thread(() -> {
while (true) {
shipmentCache.evictExpired();
sleep(300000); // 5분
}
}, "shipment-cache-cleaner");
cleaner.setDaemon(true);
cleaner.start();
}
// 2. 메트릭 수집 (데몬)
public void startMetricsCollector() {
Thread collector = new Thread(() -> {
while (true) {
collectMetrics();
sleep(60000);
}
}, "metrics-collector");
collector.setDaemon(true);
collector.start();
}
// 3. 연결 상태 확인 (데몬)
public void startHeartbeat() {
Thread heartbeat = new Thread(() -> {
while (true) {
pingExternalServices();
sleep(30000);
}
}, "heartbeat");
heartbeat.setDaemon(true);
heartbeat.start();
}
private void collectMetrics() { }
private void pingExternalServices() { }
private void sleep(long ms) { try { Thread.sleep(ms); } catch (Exception e) {} }
}
데몬 스레드의 용도는?
답:
1. 적합 용도:
공통 특징:
예:
질문:
메인 스레드가 종료되면
데몬 스레드도 즉시 종료되는가?
답:
아니다.
- 메인 종료 ≠ JVM 종료
- 다른 일반 스레드 있으면 JVM 유지
- 데몬도 계속
메인 스레드:
- 일반 스레드 (User Thread)
- 단지 첫 번째 스레드
JVM 종료 조건:
- "모든 일반 스레드 종료"
- 메인만이 아니라 모든 일반
따라서:
- 메인 종료해도
- 다른 일반 스레드 있으면 JVM 유지
public static void main(String[] args) {
Thread daemon = new Thread(() -> infiniteWork());
daemon.setDaemon(true);
daemon.start();
// main 종료
// → 일반 스레드 = main 뿐
// → 일반 스레드 0개
// → JVM 종료
// → daemon 강제 종료
}
// 이 경우 메인 종료 = 데몬 종료
public static void main(String[] args) {
Thread daemon = new Thread(() -> infiniteWork());
daemon.setDaemon(true);
daemon.start();
Thread user = new Thread(() -> {
sleep(10000); // 10초 작업
});
user.start(); // 일반 스레드
// main 종료
// → 하지만 user (일반) 살아있음
// → JVM 유지
// → daemon 계속! (즉시 종료 X)
// user 종료 (10초 후)
// → 일반 스레드 0개
// → JVM 종료
// → daemon 강제 종료
}
// 메인 종료 ≠ 데몬 종료 (user 있으니)
시나리오 2:
main: [██]
↑ main 종료 (하지만 데몬 안 끝남)
user: [████████████]
↑ user 종료 → JVM 종료
daemon: [████████████░]
↑ 여기서 강제 종료 (user 종료 시점)
핵심:
- 메인 종료가 아니라
- 모든 일반 스레드 종료가 기준
public class MainTerminationExample {
public static void main(String[] args) {
// 데몬 — 백그라운드
Thread monitor = new Thread(() -> monitorLoop());
monitor.setDaemon(true);
monitor.start();
// 일반 — 핵심 처리 (오래 걸림)
Thread processor = new Thread(() -> {
processAllShipments(); // 1시간
});
processor.start(); // 일반
// main 종료
log.info("Main thread ending");
// 하지만:
// - processor (일반) 1시간 동안 살아있음
// - JVM 유지
// - monitor (데몬) 도 1시간 계속
// processor 완료 후:
// → JVM 종료
// → monitor 강제 종료
}
static void monitorLoop() { }
static void processAllShipments() { }
}
메인 스레드 종료 시 데몬이 즉시 종료되는가?
답:
1. 아니다:
메인은 일반 스레드 중 하나:
시나리오:
기준:
데몬의 위험:
작업 완료를 보장해야 하는 작업을
데몬으로 설정하면:
- JVM 종료 시 강제 종료
- 작업 중간에 끊김
- 데이터 손실
// ❌ 위험 — 파일 저장을 데몬으로
public void saveToFileDaemon(byte[] data) {
Thread saver = new Thread(() -> {
try (FileOutputStream fos = new FileOutputStream("important.dat")) {
fos.write(data); // 쓰는 중
// ★ JVM 종료 시 강제 종료
// → 파일 일부만 쓰임 (손상)
} catch (IOException e) {
log.error("Save failed", e);
}
});
saver.setDaemon(true); // ❌ 위험!
saver.start();
// 일반 스레드 모두 종료 시
// saver 강제 종료 → 파일 손상
}
// ✓ 안전 — 일반 스레드
public void saveToFileSafe(byte[] data) {
Thread saver = new Thread(() -> {
try (FileOutputStream fos = new FileOutputStream("important.dat")) {
fos.write(data);
} catch (IOException e) {
log.error("Save failed", e);
}
});
// 데몬 설정 X (일반)
saver.start();
// 완료까지 JVM 유지 → 안전
}
// ❌ 위험 — DB 작업을 데몬으로
Thread dbWorker = new Thread(() -> {
transaction.begin();
repository.save(shipment); // 저장 중
transaction.commit(); // ★ 커밋 전 강제 종료 가능
// → 트랜잭션 미완료
// → 데이터 손실 또는 락 잔류
});
dbWorker.setDaemon(true); // ❌ 위험
// ✓ 안전 — 일반 스레드
Thread dbWorker2 = new Thread(() -> {
transaction.begin();
repository.save(shipment);
transaction.commit();
});
// 일반 (완료 보장)
데몬으로 하면 안 되는 작업:
1. 파일 쓰기
- 손상 위험
2. DB 커밋
- 트랜잭션 미완료
3. 네트워크 전송
- 부분 전송
4. 중요 계산 결과 저장
- 손실
5. 자원 정리 (close)
- 미완료
핵심:
- "완료 보장 필요" = 일반 스레드
// 데몬 대신 — Graceful Shutdown
public class GracefulShutdown {
private final ExecutorService executor = Executors.newFixedThreadPool(4);
// 일반 스레드 (데몬 X)
public GracefulShutdown() {
// 종료 훅 등록
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
log.info("Shutting down gracefully");
executor.shutdown();
try {
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
// 작업 완료 대기 후 종료
}));
}
public void submitImportantWork(Runnable work) {
executor.submit(work); // 일반 스레드 (완료 보장)
}
}
@Service
public class ShipmentSaveService {
// ❌ 위험 — 데몬으로 저장
public void saveDaemon(Shipment shipment) {
Thread saver = new Thread(() -> {
repository.save(shipment); // 중요!
});
saver.setDaemon(true); // ❌ 손실 위험
saver.start();
}
// ✓ 안전 — 일반 스레드 또는 동기
public void saveSafe(Shipment shipment) {
repository.save(shipment); // 동기 (완료 보장)
}
// ✓ 비동기지만 안전 — 일반 스레드 풀 + graceful shutdown
private final ExecutorService saveExecutor =
Executors.newFixedThreadPool(2); // 일반 스레드
public void saveAsync(Shipment shipment) {
saveExecutor.submit(() -> repository.save(shipment));
// 일반 스레드 (graceful shutdown 으로 완료 보장)
}
@PreDestroy
public void shutdown() {
saveExecutor.shutdown();
try {
saveExecutor.awaitTermination(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
saveExecutor.shutdownNow();
}
}
}
작업 완료 보장이 필요할 때 데몬을 쓰면 안 되는 이유는?
답:
1. 위험:
파일 저장 예:
부적합 작업:
해결:
데몬 여부 상속:
새 스레드의 데몬 여부 =
생성한 스레드의 데몬 여부.
일반 스레드가 만든 스레드 → 일반
데몬 스레드가 만든 스레드 → 데몬
// 일반 스레드 (메인) 가 생성
Thread userThread = new Thread(() -> {
Thread child = new Thread(task);
System.out.println(child.isDaemon()); // false (일반 상속)
child.start();
});
userThread.start();
// 데몬 스레드가 생성
Thread daemonThread = new Thread(() -> {
Thread child = new Thread(task);
System.out.println(child.isDaemon()); // true (데몬 상속)
child.start();
});
daemonThread.setDaemon(true);
daemonThread.start();
// 데몬 스레드의 자식을 일반으로
Thread daemonParent = new Thread(() -> {
Thread child = new Thread(task);
child.setDaemon(false); // 명시적으로 일반
child.start();
// 부모는 데몬이지만 자식은 일반
});
daemonParent.setDaemon(true);
daemonParent.start();
// 데몬 스레드 풀 (앱 종료 시 자동)
ThreadFactory daemonFactory = r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
};
ScheduledExecutorService monitor =
Executors.newScheduledThreadPool(2, daemonFactory);
// 모니터링 풀 (데몬)
// 일반 스레드 풀 (작업 완료 보장)
ExecutorService worker = Executors.newFixedThreadPool(4);
// 기본 일반 스레드
// shutdown 으로 graceful
// Spring 의 백그라운드 작업
@Configuration
public class TaskConfig {
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(2);
scheduler.setThreadNamePrefix("scheduled-");
scheduler.setDaemon(true); // 데몬 (앱 종료 시 자동)
return scheduler;
}
}
// @Scheduled 작업이 데몬으로
@Scheduled(fixedRate = 60000)
public void backgroundTask() {
// 보조 작업
}
@Configuration
public class ShipmentExecutorConfig {
// 데몬 — 모니터링/스케줄 (앱 종료 시 자동)
@Bean("monitorExecutor")
public ScheduledExecutorService monitorExecutor() {
return Executors.newScheduledThreadPool(2, r -> {
Thread t = new Thread(r, "monitor-" + System.nanoTime());
t.setDaemon(true); // 데몬
return t;
});
}
// 일반 — 핵심 작업 (완료 보장)
@Bean("workerExecutor")
public ExecutorService workerExecutor() {
return Executors.newFixedThreadPool(4, r -> {
Thread t = new Thread(r, "worker-" + System.nanoTime());
// 데몬 설정 X (일반)
return t;
});
}
// 일반 풀은 graceful shutdown
@PreDestroy
public void cleanup() {
// workerExecutor.shutdown() + awaitTermination
// 작업 완료 후 종료
}
}
데몬의 자식 스레드와 실무는?
답:
1. 상속:
명시적 변경:
Executor:
Spring:
| Q | 핵심 답변 |
|---|---|
| 데몬 스레드? | 일반 스레드 보조, 자동 종료 |
| 일반 vs 데몬? | 완료 보장 vs 강제 종료 |
| JVM 종료 조건? | 모든 일반 스레드 종료 |
| setDaemon 시점? | start() 전 |
| 데몬 용도? | GC, 모니터링, 로깅 |
| 메인 종료 = 데몬 종료? | 아니다 (다른 일반 있으면) |
| 파일 저장 데몬? | 위험 (손상) |
| 데몬 부적합? | 완료 보장 필요 작업 |
| 자식 스레드 데몬? | 부모 상속 |
| 안전 종료? | 일반 + Graceful Shutdown |
답:
답:
답:
답:
답:
1. 데몬 스레드
2. 용도와 기준
3. 위험
이번 Unit에서 데몬 스레드를 봤다면, 다음은 join() (Phase 3 마지막).
🚀 Phase 3 — 스레드 만들고 다루기
✅ Unit 3.1 스레드 상태 다이어그램
✅ Unit 3.2 Thread 클래스 상속
✅ Unit 3.3 Runnable 인터페이스
✅ Unit 3.4 데몬 스레드 ← 여기
⏭ Unit 3.5 join() — Phase 3 완주
✅ Phase 1 — 동시성의 기초 (4 Unit)
✅ Phase 2 — 4분면 매트릭스 (3 Unit)
🚀 Phase 3 — 스레드 다루기 (4/5 진행)
총: 11/35 Unit