관리 메뉴

Unfazedβ—οΈπŸŽ―

[Java] μŠ€λ ˆλ“œ μ’…λ£Œ & Daemon μŠ€λ ˆλ“œ λ³Έλ¬Έ

Java

[Java] μŠ€λ ˆλ“œ μ’…λ£Œ & Daemon μŠ€λ ˆλ“œ

9taetae9 2025. 2. 6. 21:26
728x90

μŠ€λ ˆλ“œ μ€‘λ‹¨μ˜ ν•„μš”μ„±κ³Ό 상황

μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ μŠ€λ ˆλ“œλŠ” μžμ›μ„ μ†ŒλΉ„ν•œλ‹€. μŠ€λ ˆλ“œκ°€ μ‹€ν–‰ 쀑이지 μ•Šλ”λΌλ„ λ©”λͺ¨λ¦¬μ™€ 컀널 μžμ›μ„ μ‚¬μš©ν•˜λ©°, μ‹€ν–‰ 쀑인 경우 CPU μ‹œκ°„μ„ ν• λ‹Ήλ°›λŠ”λ‹€. μ΄λŸ¬ν•œ μžμ› μ†ŒλΉ„λŠ” λΆˆν•„μš”ν•˜κ²Œ μ‹€ν–‰λ˜κ³  μžˆκ±°λ‚˜ 더 이상 μž‘μ—…μ„ μˆ˜ν–‰ν•˜μ§€ μ•ŠλŠ” μŠ€λ ˆλ“œκ°€ μ‘΄μž¬ν•  λ•Œ λ¬Έμ œκ°€ λœλ‹€. 특히 λ‹€μŒκ³Ό 같은 μƒν™©μ—μ„œ μŠ€λ ˆλ“œ 쀑단이 μš”κ΅¬λœλ‹€.

  1. λΆˆν•„μš”ν•œ λ¦¬μ†ŒμŠ€ 회수: μž‘μ—…μ„ μ™„λ£Œν•œ 후에도 μŠ€λ ˆλ“œκ°€ μ’…λ£Œλ˜μ§€ μ•Šμ•„ λ¦¬μ†ŒμŠ€λ₯Ό 계속 μ†ŒλΉ„ν•˜λŠ” 경우, ν•΄λ‹Ή μŠ€λ ˆλ“œλ₯Ό μ’…λ£Œν•¨μœΌλ‘œμ¨ μžμ› μ‚¬μš©μ„ μ΅œμ ν™”ν•  수 μžˆλ‹€.
  2. 비정상 λ™μž‘ λŒ€μ‘: μ„œλ²„μ— μš”μ²­μ„ λ³΄λ‚΄κ±°λ‚˜ λ³΅μž‘ν•œ 계산을 μˆ˜ν–‰ν•˜λŠ” 도쀑, μ˜ˆμƒλ³΄λ‹€ 였래 κ±Έλ¦¬λŠ” κ²½μš°λ‚˜ 응닡이 μ—†λŠ” 경우 μŠ€λ ˆλ“œλ₯Ό 쀑단해 μ‹œμŠ€ν…œ μ „μ²΄μ˜ μ•ˆμ •μ„±μ„ μœ μ§€ν•  수 μžˆλ‹€.
  3. μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ’…λ£Œ μ‹œ 처리: 메인 μŠ€λ ˆλ“œκ°€ μ’…λ£Œλ˜λ”λΌλ„ 일반(비데λͺ¬) μŠ€λ ˆλ“œκ°€ μ‹€ν–‰ 쀑이면 JVM은 μ’…λ£Œλ˜μ§€ μ•ŠλŠ”λ‹€. λ”°λΌμ„œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ’…λ£Œ 전에 λͺ¨λ“  일반 μŠ€λ ˆλ“œλ₯Ό μ μ ˆν•˜κ²Œ 정리할 ν•„μš”κ°€ μžˆλ‹€.

μΈν„°λŸ½νŠΈ(interrupt) λ©”μ„œλ“œλ₯Ό μ΄μš©ν•œ μŠ€λ ˆλ“œ 쀑단

μžλ°”μ—μ„œ μŠ€λ ˆλ“œλ₯Ό μ€‘λ‹¨ν•˜λŠ” 첫 번째 방법은 interrupt() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” 것이닀. 각 μŠ€λ ˆλ“œ κ°μ²΄λŠ” 내뢀에 "interrupted status" ν”Œλž˜κ·Έλ₯Ό κ°€μ§€κ³  있으며, interrupt() 호좜 μ‹œ ν•΄λ‹Ή ν”Œλž˜κ·Έκ°€ true둜 μ„€μ •λœλ‹€.

μΈν„°λŸ½νŠΈ λ™μž‘ 방식

  1. λΈ”λ‘œν‚Ή λ©”μ„œλ“œ λ‚΄ μΈν„°λŸ½νŠΈ: μŠ€λ ˆλ“œκ°€ sleep(), wait(), join() λ“±μ˜ λ©”μ„œλ“œλ₯Ό μ‹€ν–‰ 쀑인 경우, 이 λ©”μ„œλ“œλ“€μ€ μΈν„°λŸ½νŠΈκ°€ λ°œμƒν•˜λ©΄ μ¦‰μ‹œ InterruptedException을 λ°œμƒμ‹œν‚¨λ‹€. 예제 μ½”λ“œμ—μ„œ Thread.sleep(500000) ν˜ΈμΆœμ€ μΈν„°λŸ½νŠΈ μ‹œ μ˜ˆμ™Έλ₯Ό λ˜μ§€κ³ , μ˜ˆμ™Έ 처리 λΈ”λ‘μ—μ„œ λ©”μ‹œμ§€λ₯Ό 좜λ ₯ν•œ ν›„ μŠ€λ ˆλ“œλ₯Ό μ’…λ£Œν•œλ‹€.
  2. λͺ…μ‹œμ  μΈν„°λŸ½νŠΈ 체크: μŠ€λ ˆλ“œ λ‚΄λΆ€μ—μ„œ 주기적으둜 Thread.currentThread().isInterrupted() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ μΈν„°λŸ½νŠΈ μƒνƒœλ₯Ό ν™•μΈν•˜κ³ , μƒνƒœμ— 따라 μž‘μ—…μ„ 쀑단할 수 μžˆλ‹€. λ‘λ²ˆμ§Έ μ˜ˆμ œμ½”λ“œμ˜ LongComputationTaskμ—μ„œλŠ” 루프 λ‚΄μ—μ„œ ν•΄λ‹Ή 검사λ₯Ό μˆ˜ν–‰ν•˜μ—¬, μΈν„°λŸ½νŠΈκ°€ 감지(주석 처리 된 λΆ€λΆ„)되면 쀑간 계산 κ²°κ³Όλ₯Ό λ°˜ν™˜ν•˜κ³  μ’…λ£Œν•œλ‹€. 

λΈ”λ‘œν‚Ή λ©”μ„œλ“œ(Blocking Method) : νŠΉμ • μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” λ™μ•ˆ ν˜„μž¬ μŠ€λ ˆλ“œμ˜ 싀행을 λ©ˆμΆ”κ³ , μ–΄λ–€ 쑰건이 μΆ©μ‘±λ˜κ±°λ‚˜ νƒ€μž„μ•„μ›ƒμ΄ λ°œμƒν•  λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦¬κ²Œ λ§Œλ“œλŠ” λ©”μ„œλ“œ

  • μ‹€ν–‰ 흐름 차단: λ©”μ„œλ“œ μ™„λ£Œ/쀑단 쑰건 μΆ©μ‘± μ „κΉŒμ§€ μŠ€λ ˆλ“œ μ‹€ν–‰ μ •μ§€
  • μžμ› 점유 μœ ν˜•:
    • CPU λ¦¬μ†ŒμŠ€: λŒ€κΈ° μ‹œ 미점유 (ex: Thread.sleep())
    • μž…μΆœλ ₯ λ¦¬μ†ŒμŠ€: 물리적 μž₯치 점유 (ex: 파일 읽기/μ“°κΈ°)
  • λΈ”λ‘œν‚Ή 포인트: μΈν„°λŸ½νŠΈ κ°€λŠ₯ 지점 ν‘œμ‹œ → μŠ€λ ˆλ“œ μ œμ–΄μ˜ 핡심 μœ„μΉ˜
ꡬ뢄 λ©”μ„œλ“œ λΈ”λ‘œν‚Ή 트리거
μŠ€λ ˆλ“œ μ œμ–΄ Thread.sleep() μ‹œκ°„ 기반 λŒ€κΈ°
동기화 Object.wait() λͺ¨λ‹ˆν„° 락 ν•΄μ œ λŒ€κΈ°
μž…μΆœλ ₯ μž‘μ—… InputStream.read() 데이터 μˆ˜μ‹  λŒ€κΈ°
μŠ€λ ˆλ“œ 쑰인 Thread.join() 타 μŠ€λ ˆλ“œ μ’…λ£Œ λŒ€κΈ°
큐 μ—°μ‚° BlockingQueue.take() 큐 μš”μ†Œ 쑴재 λŒ€κΈ°

 

μΈν„°λŸ½νŠΈ 방식 μ˜ˆμ‹œ

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(new BlockingTask());
        thread.start();
        // μΈν„°λŸ½νŠΈ 호좜 μ‹œ, μŠ€λ ˆλ“œ λ‚΄λΆ€μ˜ sleep λ©”μ„œλ“œκ°€ InterruptedException을 λ°œμƒμ‹œν‚΄
        // thread.interrupt();
    }

    private static class BlockingTask implements Runnable {
        @Override
        public void run() {
            try {
                Thread.sleep(500000);
            } catch (InterruptedException e) {
                System.out.println("blocking thread νƒˆμΆœ");
            }
        }
    }
}

 

 

  • μΌ€μ΄μŠ€ 1 (μΈν„°λŸ½νŠΈ 미호좜): thread.interrupt() 호좜이 주석 처리된 μƒνƒœμ—μ„œλŠ”, BlockingTask μŠ€λ ˆλ“œκ°€ 500초 λ™μ•ˆ 슬립 μƒνƒœμ— 머무λ₯΄λ©° 메인 μŠ€λ ˆλ“œλŠ” μ’…λ£Œλ˜μ§€λ§Œ JVM은 ν•΄λ‹Ή μŠ€λ ˆλ“œκ°€ μ’…λ£Œλ˜μ§€ μ•Šμ•„ 계속 λŒ€κΈ°ν•œλ‹€.
  • μΌ€μ΄μŠ€ 2 (μΈν„°λŸ½νŠΈ 호좜): thread.interrupt()λ₯Ό ν˜ΈμΆœν•˜λ©΄, 슬립 쀑인 μŠ€λ ˆλ“œκ°€ InterruptedException을 λ°œμƒμ‹œν‚€κ³ , μ˜ˆμ™Έ 처리 블둝 λ‚΄ λ©”μ‹œμ§€λ₯Ό 좜λ ₯ν•œ ν›„ μŠ€λ ˆλ“œκ°€ μ’…λ£Œλœλ‹€.

 

μΈν„°λŸ½νŠΈ 방식은 μŠ€λ ˆλ“œκ°€ λΈ”λ‘œν‚Ή λ©”μ„œλ“œλ₯Ό 호좜 쀑일 λ•Œ μ¦‰κ°μ μœΌλ‘œ μ’…λ£Œν•  수 μžˆλŠ” μž₯점을 μ œκ³΅ν•œλ‹€. λ˜ν•œ, λͺ…μ‹œμ μœΌλ‘œ μƒνƒœλ₯Ό μ²΄ν¬ν•˜λŠ” 방법을 μ‚¬μš©ν•˜λ©΄ μŠ€λ ˆλ“œκ°€ μ–΄λ–€ μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” 도쀑에도 쀑단 μ—¬λΆ€λ₯Ό 확인할 수 μžˆλ‹€.

 

데λͺ¬ μŠ€λ ˆλ“œ(daemon thread)λ₯Ό μ΄μš©ν•œ μŠ€λ ˆλ“œ 쀑단

데λͺ¬ μŠ€λ ˆλ“œλŠ” JVM의 μ’…λ£Œ 쑰건과 κ΄€λ ¨ν•˜μ—¬ μ€‘μš”ν•œ 역할을 ν•œλ‹€. 기본적으둜 μƒμ„±λœ μŠ€λ ˆλ“œλŠ” 일반 μŠ€λ ˆλ“œλ‘œ κ°„μ£Όλ˜λ©°, λͺ¨λ“  일반 μŠ€λ ˆλ“œκ°€ μ’…λ£Œλ˜μ–΄μ•Ό JVM이 μ’…λ£Œλœλ‹€. 데λͺ¬ μŠ€λ ˆλ“œλŠ” 메인 μŠ€λ ˆλ“œμ™€ 달리 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μ’…λ£Œλ₯Ό λ°©ν•΄ν•˜μ§€ μ•Šλ„λ‘ μ„€κ³„λ˜μ–΄ μžˆλ‹€.

데λͺ¬ μŠ€λ ˆλ“œ μ‚¬μš© μ‹œ κ³ λ € 사항

  • μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ’…λ£Œ: μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ’…λ£Œ μ‹œ, 데λͺ¬ μŠ€λ ˆλ“œλŠ” μžλ™μœΌλ‘œ μ’…λ£Œλœλ‹€. λ”°λΌμ„œ λ°±κ·ΈλΌμš΄λ“œ μž‘μ—…μ΄λ‚˜ μž₯μ‹œκ°„ μ‹€ν–‰λ˜λŠ” 계산 μž‘μ—…μ—μ„œ 데λͺ¬ μŠ€λ ˆλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄, 메인 μŠ€λ ˆλ“œ μ’…λ£Œμ™€ ν•¨κ»˜ λΆˆν•„μš”ν•œ λ¦¬μ†ŒμŠ€ μ†Œλͺ¨λ₯Ό λ°©μ§€ν•  수 μžˆλ‹€.
  • μ‹€μ œ μ‘μš© 사둀: μ›Ή μ„œλ²„μ˜ νƒ€μž„μ•„μ›ƒ 처리, μ•ˆλ“œλ‘œμ΄λ“œμ˜ λ°±κ·ΈλΌμš΄λ“œ μž‘μ—… μ·¨μ†Œ, λΆ„μ‚° μ‹œμŠ€ν…œμ—μ„œμ˜ λΈ”λ‘œν‚Ή 호좜 관리 λ“± λ‹€μ–‘ν•œ λΆ„μ•Όμ—μ„œ 데λͺ¬ μŠ€λ ˆλ“œλ₯Ό ν™œμš©ν•œλ‹€.

데λͺ¬ μŠ€λ ˆλ“œ 방식 μ˜ˆμ‹œ

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new LongComputationTask(new BigInteger("999999"), new BigInteger("999999999")));
        // μŠ€λ ˆλ“œλ₯Ό 데λͺ¬ μŠ€λ ˆλ“œλ‘œ μ„€μ •ν•˜μ—¬, 메인 μŠ€λ ˆλ“œ μ’…λ£Œ μ‹œ ν•¨κ»˜ μ’…λ£Œλ˜λ„λ‘ 함
        thread.setDaemon(true);
        thread.start();
        // ν•„μš”μ— 따라 일정 μ‹œκ°„ ν›„ 메인 μŠ€λ ˆλ“œκ°€ μ’…λ£Œλ˜λ„λ‘ μ„€μ •ν•  수 있음
        // Thread.sleep(10000);
        // λ˜λŠ” thread.interrupt() ν˜ΈμΆœν•˜μ—¬ 쀑단할 μˆ˜λ„ 있음
    }

    private static class LongComputationTask implements Runnable {
        private BigInteger base;
        private BigInteger power;

        public LongComputationTask(BigInteger base, BigInteger power) {
            this.base = base;
            this.power = power;
        }

        @Override
        public void run() {
            System.out.println(base + "^" + power + " = " + pow(base, power));
        }

        private BigInteger pow(BigInteger base, BigInteger power) {
            BigInteger result = BigInteger.ONE;
            // 루프 λ‚΄μ—μ„œ μΈν„°λŸ½νŠΈ μƒνƒœλ₯Ό ν™•μΈν•˜μ—¬, 쀑간에 μ’…λ£Œν•  수 μžˆλ„λ‘ 처리(μ‹œκ°„μ΄ μ˜€λž˜κ±Έλ¦¬λŠ” λΆ€λΆ„)
            for (BigInteger i = BigInteger.ZERO; i.compareTo(power) != 0; i = i.add(BigInteger.ONE)) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("Prematurely interrupted LongComputationTask");
                    return result;
                }
                result = result.multiply(base);
            }
            return result;
        }
    }
}

 

ν•΄λ‹Ή μ˜ˆμ‹œμ—μ„œλŠ” LongComputationTaskκ°€ 맀우 큰 μ§€μˆ˜ 계산을 μˆ˜ν–‰ν•œλ‹€. 루프 λ‚΄λΆ€μ—μ„œ μΈν„°λŸ½νŠΈ μ—¬λΆ€λ₯Ό ν™•μΈν•˜μ—¬, λ§Œμ•½ μΈν„°λŸ½νŠΈκ°€ λ°œμƒν•˜λ©΄ 쀑간 κ²°κ³Όλ₯Ό λ°˜ν™˜ν•˜κ³  μ’…λ£Œν•œλ‹€. 데λͺ¬ μŠ€λ ˆλ“œλ‘œ μ„€μ •ν•¨μœΌλ‘œμ¨ 메인 μŠ€λ ˆλ“œκ°€ μ’…λ£Œλ˜λ©΄ ν•΄λ‹Ή μŠ€λ ˆλ“œ μ—­μ‹œ JVM에 μ˜ν•΄ κ°•μ œ μ’…λ£Œλ˜λ―€λ‘œ, μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ’…λ£Œλ₯Ό μœ„ν•œ 좔가적인 μŠ€λ ˆλ“œ 관리가 ν•„μš”ν•˜μ§€ μ•Šλ‹€.

 

정리

μŠ€λ ˆλ“œ 쀑단은 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μ•ˆμ •μ„±κ³Ό 효율적인 μžμ› 관리에 핡심적인 역할을 ν•œλ‹€.

μΈν„°λŸ½νŠΈ λ©”μ„œλ“œλ₯Ό ν†΅ν•œ μŠ€λ ˆλ“œ 쀑단은 λΈ”λ‘œν‚Ή λ©”μ„œλ“œλ‚˜ 루프 λ‚΄λΆ€μ—μ„œ μΈν„°λŸ½νŠΈ μƒνƒœλ₯Ό μ²΄ν¬ν•˜λŠ” λ°©μ‹μœΌλ‘œ μŠ€λ ˆλ“œμ˜ 정상 μ’…λ£Œλ₯Ό 보μž₯ν•œλ‹€.

반면, 데λͺ¬ μŠ€λ ˆλ“œλ₯Ό ν™œμš©ν•˜λ©΄ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ’…λ£Œ μ‹œ λ°±κ·ΈλΌμš΄λ“œ μŠ€λ ˆλ“œλ‘œ μΈν•œ μ§€μ—° 없이 μžμ› νšŒμˆ˜κ°€ κ°€λŠ₯ν•˜λ‹€.

 

참고 자료:

Udemy - Java λ©€ν‹°μŠ€λ ˆλ”©, 병행성 및 병렬 μ‹€ν–‰ ν”„λ‘œκ·Έλž˜λ° μ „λ¬Έκ°€ 되기

728x90