Skip to content

Latest commit

Β 

History

History
403 lines (288 loc) Β· 18.7 KB

item-81-wait-notify.md

File metadata and controls

403 lines (288 loc) Β· 18.7 KB

item 81 : wait와 notifyλ³΄λ‹€λŠ” λ™μ‹œμ„± μœ ν‹Έλ¦¬ν‹°λ₯Ό μ• μš©ν•˜λΌ

λ“€μ–΄κ°€κΈ°

μ§€κΈˆμ€ wait와 notifyλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•  μ΄μœ κ°€ 많이 μ€„μ—ˆλ‹€. μžλ°” 5μ—μ„œ λ„μž…λœ κ³ μˆ˜μ€€μ˜ λ™μ‹œμ„± μœ ν‹Έλ¦¬ν‹°κ°€ 이전이라면 wait와 notify둜 ν•˜λ“œμ½”λ”©ν•΄μ•Ό ν–ˆλ˜ μ „ν˜•μ μΈ 일듀을 λŒ€μ‹  μ²˜λ¦¬ν•΄μ£ΌκΈ° λ•Œλ¬Έμ΄λ‹€. wait와 notifiyλŠ” μ˜¬λ°”λ₯΄κ²Œ μ‚¬μš©ν•˜κΈ°κ°€ μ•„μ£Ό κΉŒλ‹€λ‘œμš°λ‹ˆ κ³ μˆ˜μ€€ λ™μ‹œμ„± μœ ν‹Έλ¦¬ν‹°λ₯Ό μ‚¬μš©ν•˜μž.

java.util.concurrent νŒ¨ν‚€μ§€

java.util.concurrent νŒ¨ν‚€μ§€λŠ” κ³ μˆ˜μ€€μ˜ λ™μ‹œμ„± μœ ν‹Έλ¦¬ν‹°λ₯Ό μ œκ³΅ν•œλ‹€. 이 νŒ¨ν‚€μ§€λŠ” 크게 μ„Έ 가지 μ£Όμš” λ²”μ£Όλ‘œ λ‚˜λˆŒ 수 μžˆλ‹€:

  1. μ‹€ν–‰μž ν”„λ ˆμž„μ›Œν¬ (Executor Framework)
  2. λ™μ‹œμ„± μ»¬λ ‰μ…˜ (Concurrent Collections)
  3. 동기화 μž₯치 (Synchronizers)

각각의 κΈ°λŠ₯κ³Ό λ™μž‘ 방식을 μžμ„Ένžˆ μ‚΄νŽ΄λ³΄μž.


1. λ™μ‹œμ„± μ»¬λ ‰μ…˜ (Concurrent Collections)

λ™μ‹œμ„± μ»¬λ ‰μ…˜μ€ List, Queue, Mapκ³Ό 같은 ν‘œμ€€ μ»¬λ ‰μ…˜ μΈν„°νŽ˜μ΄μŠ€μ— λ™μ‹œμ„± κΈ°λŠ₯을 μΆ”κ°€ν•œ μ»¬λ ‰μ…˜μ΄λ‹€.

  • λ‚΄λΆ€μ μœΌλ‘œ 동기화λ₯Ό μˆ˜ν–‰ν•˜λ©°, μ™ΈλΆ€μ—μ„œ λ³„λ„λ‘œ 락을 κ±Έμ–΄ 동기화λ₯Ό μ‹œλ„ν•˜λ©΄ 였히렀 μ„±λŠ₯이 μ €ν•˜λœλ‹€. 즉, λ™μ‹œμ„± μ»¬λ ‰μ…˜μ—μ„œ λ™μ‹œμ„±μ„ 무λ ₯ν™”ν•˜λŠ” 건 λΆˆκ°€λŠ₯ν•˜λ©° , μ™ΈλΆ€μ—μ„œ 락을 μΆ”κ°€λ‘œ μ‚¬μš©ν•˜λ©΄ 였히렀 속도가 느렀 진닀
  • μ—¬λŸ¬ λ©”μ„œλ“œλ₯Ό μ›μžμ μœΌλ‘œ λ¬Άμ–΄ ν˜ΈμΆœν•˜λŠ” 일 μ—­μ‹œ λΆˆκ°€λŠ₯ν•˜λ‹€.
  • λ™κΈ°ν™”λœ μ»¬λ ‰μ…˜ λŒ€μ‹  λ™μ‹œμ„± μ»¬λ ‰μ…˜μ„ μ‚¬μš©ν•˜λŠ” 것이 더 νš¨μœ¨μ μ΄λ‹€.

μƒνƒœ 의쑴적 λ©”μ„œλ“œ

λ™μ‹œμ„± μ»¬λ ‰μ…˜μ€ μ›μžμ μœΌλ‘œ μ—¬λŸ¬ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λŠ” 것이 λΆˆκ°€λŠ₯ν•˜λ‹€. 이 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ μ—¬λŸ¬ λ™μž‘μ„ ν•˜λ‚˜μ˜ μ›μžμ  λ™μž‘μœΌλ‘œ λ¬ΆλŠ” μƒνƒœ 의쑴적 λ©”μ„œλ“œκ°€ μΆ”κ°€λ˜μ—ˆλ‹€.

putIfAbsent λ©”μ„œλ“œ μ˜ˆμ‹œ

이 λ©”μ„œλ“œ 덕에 μŠ€λ ˆλ“œ μ•ˆμ „ν•œ μ •κ·œν™” 맡 (canonicalizing map)을 μ‰½κ²Œ κ΅¬ν˜„ν•  수 μžˆλ‹€.

private static final ConcurrentMap<String, String> map = new ConcurrentHashMap<>();

public static String intern(String s) {
    String previousValue = map.putIfAbsent(s, s);
    return previousValue == null ? s : previousValue;
}

μ„€λͺ…

  1. map.putIfAbsent(s, s)
    • sλΌλŠ” ν‚€κ°€ μ‘΄μž¬ν•˜μ§€ μ•ŠμœΌλ©΄ sλ₯Ό 맡에 μΆ”κ°€ν•˜κ³  null을 λ°˜ν™˜
    • sλΌλŠ” ν‚€κ°€ 이미 μ‘΄μž¬ν•˜λ©΄ κΈ°μ‘΄ 값을 λ°˜ν™˜
  2. return λ¬Έ
    • previousValueκ°€ null인 경우 β†’ μž…λ ₯된 λ¬Έμžμ—΄ sλ₯Ό λ°˜ν™˜.
    • previousValueκ°€ null이 μ•„λ‹Œ 경우 β†’ 이미 μ‘΄μž¬ν•˜λŠ” 값을 λ°˜ν™˜.

아직 κ°œμ„ ν•  게 λ‚¨μ•˜λ‹€. ConcurrentHashMap은 get 같은 검색 κΈ°λŠ₯에 μ΅œμ ν™”λ˜μ—ˆλ‹€. λ”°λΌμ„œ get을 λ¨Όμ € ν˜ΈμΆœν•˜μ—¬ ν•„μš”ν•  λ•Œλ§Œ putlfAbsentλ₯Ό ν˜ΈμΆœν•˜λ©΄ 더 λΉ λ₯΄λ‹€.

putIfAbsentλŠ” ConcurrentMap에 μΆ”κ°€λœ λ©”μ„œλ“œλ‘œ, ν‚€κ°€ 없을 λ•Œλ§Œ 값을 μΆ”κ°€ν•˜λŠ” λ™μž‘μ„ μˆ˜ν–‰ν•œλ‹€. κΈ°μ‘΄ 값이 있으면 κ·Έ 값을 λ°˜ν™˜ν•˜κ³ , μ—†μœΌλ©΄ null을 λ°˜ν™˜ν•œλ‹€.

μ•„λž˜λŠ” String.intern λ©”μ„œλ“œλ₯Ό ConcurrentHashMap으둜 흉내 λ‚Έ 예제 μ½”λ“œλ‹€:

// ConcurrentHashMap을 μ‚¬μš©ν•΄ λ¬Έμžμ—΄μ„ μ •κ·œν™”ν•˜λŠ” λ©”μ„œλ“œ
private static final ConcurrentMap<String, String> map = new ConcurrentHashMap<>();

public static String intern(String s) {
    // κΈ°μ‘΄ 값을 κ°€μ Έμ˜΄
    String result = map.get(s);
    if (result == null) {
        // ν‚€κ°€ μ—†μœΌλ©΄ 값을 μΆ”κ°€
        result = map.putIfAbsent(s, s);
        if (result == null) {
            result = s;
        }
    }
    return result;
}

μ½”λ“œ μ„€λͺ…:

  • get: λ¨Όμ € 킀에 ν•΄λ‹Ήν•˜λŠ” 값을 ν™•μΈν•œλ‹€.
  • putIfAbsent: ν‚€κ°€ μ—†μœΌλ©΄ 값을 μΆ”κ°€ν•˜λ©°, μ›μžμ μœΌλ‘œ μ‹€ν–‰λœλ‹€.
  • κ²°κ³Ό: μŠ€λ ˆλ“œ μ•ˆμ „ν•œ μ •κ·œν™” 맡을 κ΅¬ν˜„ν•  수 μžˆλ‹€.

Tip: ConcurrentHashMap은 검색 κΈ°λŠ₯에 μ΅œμ ν™”λ˜μ–΄ μžˆμœΌλ―€λ‘œ, λ¨Όμ € get을 ν˜ΈμΆœν•˜λ©΄ μ„±λŠ₯이 λ”μš± ν–₯μƒλœλ‹€.

μ™œ λ™μ‹œμ„± μ»¬λ ‰μ…˜μ΄ μ€‘μš”ν•œκ°€?

  • ConcurrentHashMap은 λ™μ‹œμ„±μ΄ λ›°μ–΄λ‚˜λ©° 속도도 무척 λΉ λ₯΄λ‹€.
    • λ‚΄ μ»΄ν“¨ν„°μ—μ„œ 이 λ©”μ„œλ“œλŠ” String, intern 보닀 6λ°°λ‚˜ λΉ λ₯΄λ‹€(ν•˜μ§€λ§Œ String, intern μ—λŠ” 였래 μ‹€ν–‰ λ˜λŠ” ν”„λ‘œκ·Έλž¨ μ—μ„œ λ©”λͺ¨λ¦¬ λˆ„μˆ˜λ₯Ό λ°©μ§€ν•˜λŠ” κΈ°μˆ λ„ λ“€μ–΄κ°€ μžˆμŒμ„ κ°μ•ˆν•˜μž)
  • Collections.synchronizedMap λŒ€μ‹  ConcurrentHashMap을 μ‚¬μš©ν•˜λ©΄ μ„±λŠ₯이 크게 κ°œμ„ λœλ‹€.
    • λ™κΈ°ν™”λœ 맡을 λ™μ‹œμ„± 맡으둜 κ΅μ²΄ν•˜λŠ” κ²ƒλ§ŒμœΌλ‘œ λ™μ‹œμ„± μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μ„±λŠ₯은 극적으둜 κ°œμ„ λœλ‹€.
  • λ‚΄λΆ€ 동기화λ₯Ό 톡해 높은 λ™μ‹œμ„±μ„ μ œκ³΅ν•˜λ©°, μ™ΈλΆ€μ—μ„œ 좔가적인 동기화가 ν•„μš”ν•˜μ§€ μ•Šλ‹€.

μ»¬λ ‰μ…˜ μΈν„°νŽ˜μ΄μŠ€ 쀑 μΌλΆ€λŠ” μž‘μ—…μ΄ μ„±κ³΅μ μœΌλ‘œ μ™„λ£Œλ  λ•ŒκΉŒμ§€ 기닀리도둝 (즉, μ°¨λ‹¨λ˜λ„λ‘) ν™•μž₯λ˜μ—ˆλ‹€.

BlockingQueue와 CountDownLatch 정리 및 예제 μ½”λ“œ

1. BlockingQueue

μ •μ˜:
BlockingQueueλŠ” 큐의 ν™•μž₯으둜, λΉ„μ–΄μžˆκ±°λ‚˜ 가득 μ°¬ μƒνƒœμ— 따라 μž‘μ—…μ΄ μ°¨λ‹¨λ˜λ„λ‘ μ§€μ›ν•œλ‹€.

  • μƒμ‚°μž-μ†ŒλΉ„μž νŒ¨ν„΄μ— 맀우 μœ μš©ν•˜λ©°, μŠ€λ ˆλ“œ κ°„ μ•ˆμ „ν•˜κ²Œ 데이터λ₯Ό κ³΅μœ ν•  수 μžˆλ„λ‘ μ„€κ³„λ˜μ—ˆλ‹€.
  • take() λ©”μ„œλ“œλŠ” 큐가 λΉ„μ–΄ μžˆμ„ 경우 μ›μ†Œκ°€ 좔가될 λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦°λ‹€.
  • put() λ©”μ„œλ“œλŠ” 큐가 가득 찼을 경우 곡간이 생길 λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦°λ‹€.

BlockingQueue 예제 μ½”λ“œ: μƒμ‚°μž-μ†ŒλΉ„μž νŒ¨ν„΄

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class ProducerConsumerExample {
    private static final int CAPACITY = 5; // 큐 μš©λŸ‰ μ„€μ •
    private static BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(CAPACITY);

    public static void main(String[] args) {
        // μƒμ‚°μž μŠ€λ ˆλ“œ
        Thread producer = new Thread(() -> {
            try {
                for (int i = 1; i <= 10; i++) {
                    System.out.println("μƒμ‚°μž: " + i + " μΆ”κ°€");
                    queue.put(i); // 큐에 μ›μ†Œλ₯Ό μΆ”κ°€ (가득 μ°¨λ©΄ λŒ€κΈ°)
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        // μ†ŒλΉ„μž μŠ€λ ˆλ“œ
        Thread consumer = new Thread(() -> {
            try {
                while (true) {
                    Integer value = queue.take(); // νμ—μ„œ μ›μ†Œλ₯Ό 꺼냄 (λΉ„μ–΄μžˆμœΌλ©΄ λŒ€κΈ°)
                    System.out.println("μ†ŒλΉ„μž: " + value + " 처리");
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        producer.start();
        consumer.start();
    }
}

2. CountDownLatch

μ •μ˜:
CountDownLatchλŠ” μΌνšŒμ„± 동기화 μž₯치둜, μ§€μ •λœ μž‘μ—…μ΄ μ™„λ£Œλ  λ•ŒκΉŒμ§€ λ‹€λ₯Έ μŠ€λ ˆλ“œμ˜ 싀행을 μ°¨λ‹¨ν•œλ‹€.

  • countDown(): 호좜될 λ•Œλ§ˆλ‹€ μΉ΄μš΄νŠΈκ°€ κ°μ†Œν•œλ‹€.
  • await(): μΉ΄μš΄νŠΈκ°€ 0이 될 λ•ŒκΉŒμ§€ ν˜„μž¬ μŠ€λ ˆλ“œλ₯Ό μ°¨λ‹¨ν•œλ‹€.

CountDownLatchλŠ” μΌνšŒμ„± μž₯벽으둜, ν•˜λ‚˜ μ΄μƒμ˜ μŠ€λ ˆλ“œκ°€ 또 λ‹€λ₯Έ ν•˜λ‚˜ μ΄μƒμ˜ μŠ€λ ˆλ“œ μž‘μ—…μ΄ 끝날 λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦¬κ²Œ ν•˜λŠ” 역할을 ν•œλ‹€.

μƒμ„±μžλ‘œ λ°›λŠ” int 값을 λ°›μœΌλ©°, 이 값이 countDown λ©”μ„œλ“œλ₯Ό λͺ‡ 번 ν˜ΈμΆœν•΄μ•Ό λŒ€κΈ° 쀑인 μŠ€λ ˆλ“œλ₯Ό κΉ¨μš°λŠ”μ§€λ₯Ό κ²°μ •ν•œλ‹€.

CountDownLatch 예제 μ½”λ“œ

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) {
        int numberOfWorkers = 3;
        CountDownLatch latch = new CountDownLatch(numberOfWorkers);

        // μž‘μ—…μž μŠ€λ ˆλ“œ
        for (int i = 1; i <= numberOfWorkers; i++) {
            new Thread(new Worker(latch, i)).start();
        }

        try {
            latch.await(); // λͺ¨λ“  μž‘μ—…μž μŠ€λ ˆλ“œμ˜ μž‘μ—…μ΄ 끝날 λ•ŒκΉŒμ§€ λŒ€κΈ°
            System.out.println("λͺ¨λ“  μž‘μ—… μ™„λ£Œ. 메인 μŠ€λ ˆλ“œ 진행.");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    static class Worker implements Runnable {
        private final CountDownLatch latch;
        private final int workerId;

        Worker(CountDownLatch latch, int workerId) {
            this.latch = latch;
            this.workerId = workerId;
        }

        @Override
        public void run() {
            System.out.println("μž‘μ—…μž " + workerId + " μž‘μ—… μ‹œμž‘");
            try {
                Thread.sleep(1000 * workerId); // 각 μŠ€λ ˆλ“œκ°€ λ‹€λ₯Έ μ‹œκ°„μ— μ’…λ£Œλ˜λ„λ‘ μ„€μ •
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("μž‘μ—…μž " + workerId + " μž‘μ—… μ™„λ£Œ");
            latch.countDown(); // 카운트 λ‹€μš΄
        }
    }
}

πŸ“š 정리

BlockingQueue

  • νŠΉμ§•: 큐가 λΉ„μ–΄μžˆκ±°λ‚˜ 가득 μ°¨λ©΄ μž‘μ—…μ΄ 차단됨.
  • μ‚¬μš© 사둀:
    • μƒμ‚°μž-μ†ŒλΉ„μž νŒ¨ν„΄
    • μž‘μ—… 큐 (ThreadPoolExecutor λ‚΄λΆ€μ—μ„œ μ‚¬μš©λ¨)

CountDownLatch

  • νŠΉμ§•: μ§€μ •λœ 개수만큼 countDown()이 호좜될 λ•ŒκΉŒμ§€ λ‹€λ₯Έ μŠ€λ ˆλ“œμ˜ μž‘μ—…μ„ 차단함.
  • μ‚¬μš© 사둀:
    • λ‹€μˆ˜μ˜ μž‘μ—…μ„ λ³‘λ ¬λ‘œ μ²˜λ¦¬ν•œ ν›„ ν•˜λ‚˜μ˜ μž‘μ—…μ„ μ‹œμž‘ν•  λ•Œ
    • νŠΉμ • μŠ€λ ˆλ“œλ“€μ΄ λͺ¨λ‘ μ™„λ£Œλ  λ•ŒκΉŒμ§€ λŒ€κΈ°ν•  λ•Œ

2. 동기화 μž₯치 (Synchronizers)

동기화 μž₯μΉ˜λŠ” μ—¬λŸ¬ μŠ€λ ˆλ“œ κ°„μ˜ μž‘μ—…μ„ μ‘°μœ¨ν•˜κ±°λ‚˜ ν˜‘λ ₯ν•  수 μžˆλ„λ‘ λ„μ™€μ£ΌλŠ” 도ꡬ이닀. λŒ€ν‘œμ μΈ 동기화 μž₯μΉ˜λŠ” λ‹€μŒκ³Ό κ°™λ‹€:

  1. CountDownLatch: μ—¬λŸ¬ μŠ€λ ˆλ“œμ˜ μž‘μ—…μ΄ 끝날 λ•ŒκΉŒμ§€ λŒ€κΈ°.
  2. Semaphore: μ§€μ •λœ 수의 μŠ€λ ˆλ“œλ§Œ λ™μ‹œμ— μ ‘κ·Όν•  수 μžˆλ„λ‘ μ œν•œ.
  3. CyclicBarrier: μ—¬λŸ¬ μŠ€λ ˆλ“œκ°€ λ™μ‹œμ— λ§Œλ‚˜λ„λ‘ 쑰율.
  4. Exchanger: 두 μŠ€λ ˆλ“œ κ°„ 데이터λ₯Ό κ΅ν™˜.
  5. Phaser: 닀쀑 단계 μž‘μ—… μ‘°μœ¨μ— μ‚¬μš©λ˜λŠ” κ°•λ ₯ν•œ 동기화 μž₯치.

CountDownLatch μ‚¬μš©λ²•

CountDownLatchλŠ” μΌνšŒμ„± 동기화 μž₯치둜, 정해진 횟수만큼 countDown()이 호좜될 λ•ŒκΉŒμ§€ μŠ€λ ˆλ“œκ°€ λŒ€κΈ°ν•œλ‹€. 즉, ν•˜λ‚˜ μ΄μƒμ˜ μŠ€λ ˆλ“œκ°€ 또 λ‹€λ₯Έ ν•˜λ‚˜ μ΄μƒμ˜ μŠ€λ ˆλ“œ μž‘μ—…μ΄ 끝날 λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦¬κ²Œ ν•œλ‹€.

λ™μ‹œ μ‹€ν–‰ μ‹œκ°„ μΈ‘μ • 예제

이 ν”„λ ˆμž„μ›Œν¬λŠ” λ©”μ„œλ“œ ν•˜λ‚˜λ‘œ κ΅¬μ„±λ˜λ©°, 이 λ©”μ„œλ“œλŠ” λ™μž‘λ“€μ„ μ‹€ν–‰ν•  μ‹€ν–‰μžμ™€ λ™μž‘μ„ λͺ‡ κ°œλ‚˜ λ™μ‹œμ— μˆ˜ν–‰ ν•  수 μžˆλŠ”μ§€λ₯Ό λœ»ν•˜λŠ” λ™μ‹œμ„± μˆ˜μ€€(concurrency)을 λ§€κ°œλ³€μˆ˜λ‘œ λ°›λŠ”λ‹€.

타이머 μŠ€λ ˆλ“œκ°€ μ‹œκ³„λ₯Ό μ‹œμž‘ν•˜κΈ° 전에 λͺ¨λ“  μž‘μ—…μž μŠ€λ ˆλ“œλŠ” λ™μž‘μ„ μˆ˜ν–‰ν•  μ€€λΉ„ λ₯Ό λ§ˆμΉœλ‹€. λ§ˆμ§€λ§‰ μž‘μ—…μž μŠ€λ ˆλ“œκ°€ μ€€λΉ„λ₯Ό 마치면 타이머 μŠ€λ ˆλ“œκ°€ β€˜μ‹œμž‘ 방아쇠’λ₯Ό 당겨 μž‘μ—…μž μŠ€λ ˆλ“œλ“€μ΄ 일을 μ‹œμž‘ν•˜κ²Œ ν•œλ‹€. λ§ˆμ§€λ§‰ μž‘μ—…μž μŠ€λ ˆλ“œκ°€ λ™μž‘μ„ 마치자마자 타이머 μŠ€λ ˆλ“œλŠ” μ‹œκ³„λ₯Ό λ©ˆμΆ˜λ‹€. μ΄μƒμ˜ κΈ°λŠ₯을 wait 와 notify만으둜 κ΅¬ν˜„ν•˜λ €λ©΄ μ•„μ£Ό λ‚œν•΄ν•˜κ³  μ§€μ €λΆ„ν•œ μ½”λ“œκ°€ νƒ„μƒν•˜μ§€λ§Œ, CountDownLatchλ₯Ό μ“°λ©΄ λ†€λžλ„λ‘ μ§κ΄€μ μœΌλ‘œ κ΅¬ν˜„ν•  수 μžˆλ‹€.

μ•„λž˜λŠ” μ—¬λŸ¬ μž‘μ—…μžμ˜ μ€€λΉ„ 및 λ™μ‹œ μ‹€ν–‰ μ‹œκ°„μ„ μΈ‘μ •ν•˜λŠ” μ½”λ“œμ΄λ‹€:

import java.util.concurrent.*;

public class CountDownLatchTest {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        try {
            long result = time(executorService, 3, () -> System.out.println("Hello"));
            System.out.println("Execution time: " + result + " nanoseconds");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }

    public static long time(Executor executor, int concurrency, Runnable action) throws InterruptedException {
        CountDownLatch ready = new CountDownLatch(concurrency);
        CountDownLatch start = new CountDownLatch(1);
        CountDownLatch done = new CountDownLatch(concurrency);

        for (int i = 0; i < concurrency; i++) {
            executor.execute(() -> {
                ready.countDown();
                try {
                    start.await();
                    action.run();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    done.countDown();
                }
            });
        }

        ready.await(); // λͺ¨λ“  μž‘μ—…μžκ°€ 쀀비될 λ•ŒκΉŒμ§€ λŒ€κΈ°
        long startNanos = System.nanoTime();
        start.countDown(); // μž‘μ—… μ‹œμž‘ μ‹ ν˜Έ
        done.await(); // λͺ¨λ“  μž‘μ—…μ΄ μ™„λ£Œλ  λ•ŒκΉŒμ§€ λŒ€κΈ°
        return System.nanoTime() - startNanos;
    }
}

μ½”λ“œ μ„€λͺ…:

  • ready: μž‘μ—…μžλ“€μ΄ μ€€λΉ„λ˜μ—ˆμŒμ„ μ•Œλ¦Ό.
  • start: λͺ¨λ“  μž‘μ—…μžμ—κ²Œ μž‘μ—… μ‹œμž‘ μ‹ ν˜Έλ₯Ό 보냄.
  • done: μž‘μ—… μ™„λ£Œ μ•Œλ¦Ό.
  • κ²°κ³Ό: μ—¬λŸ¬ μŠ€λ ˆλ“œλ₯Ό λ™μ‹œμ— μ‹œμž‘ν•˜κ³  μ™„λ£Œ μ‹œκ°„μ„ μ •ν™•νžˆ μΈ‘μ •ν•œλ‹€.

μ‹œκ°„ 간격을 잴 λ•ŒλŠ” 항상 System.currentTiune1Vlillisκ°€ μ•„λ‹Œ System.nanoTime 을 μ‚¬μš©ν•˜μž. System.nanoTime은 더 μ •ν™•ν•˜κ³  μ •λ°€ν•˜λ©° μ‹œμŠ€ν…œμ˜ μ‹€μ‹œκ°„ μ‹œκ³„ 의 μ‹œκ°„ 보정에 영ν–₯받지 μ•ŠλŠ”λ‹€.

λΆ€κ°€ μ„€λͺ…

CountDownLatchλ₯Ό 3개 μ‚¬μš©ν•˜μ—¬ 타이머 μŠ€λ ˆλ“œμ™€ μž‘μ—…μž μŠ€λ ˆλ“œ κ°„μ˜ 동기화λ₯Ό κ΄€λ¦¬ν•œλ‹€.

  1. ready 래치: μž‘μ—…μž μŠ€λ ˆλ“œκ°€ μ€€λΉ„κ°€ μ™„λ£Œλ˜μ—ˆμŒμ„ 타이머 μŠ€λ ˆλ“œμ— μ•Œλ¦Ό.
  2. start 래치: 타이머 μŠ€λ ˆλ“œκ°€ λͺ¨λ“  μž‘μ—…μž μŠ€λ ˆλ“œμ˜ μ€€λΉ„λ₯Ό ν™•μΈν•œ ν›„, μž‘μ—… μ‹œμž‘μ„ μ•Œλ¦Ό.
  3. done 래치: μž‘μ—…μž μŠ€λ ˆλ“œκ°€ μž‘μ—…μ„ μ™„λ£Œν–ˆμŒμ„ 타이머 μŠ€λ ˆλ“œμ— μ•Œλ¦Ό.

μŠ€λ ˆλ“œμ˜ μ€€λΉ„ β†’ μž‘μ—… μ‹œμž‘ β†’ μž‘μ—… μ™„λ£Œ 과정을 μ •ν™•ν•œ μ‹œμ μ— λ§žμΆ”μ–΄ 동기화


3. wait와 notify μ‚¬μš© μ‹œ μ£Όμ˜μ‚¬ν•­

1. λŒ€κΈ° 반볡문(wait loop) κ΄€μš©κ΅¬λ₯Ό μ‚¬μš©ν•˜λΌ

  • 반볡문 μ•ˆμ—μ„œ wait()λ₯Ό ν˜ΈμΆœν•΄μ•Ό ν•œλ‹€.
  • λŒ€κΈ° μ „: 쑰건이 이미 μΆ©μ‘±λ˜μ—ˆλ‹€λ©΄ wait()λ₯Ό κ±΄λ„ˆλ›΄λ‹€ β†’ 응닡 λΆˆκ°€ μƒνƒœ 예방
  • λŒ€κΈ° ν›„: 쑰건이 μΆ©μ‘±λ˜μ§€ μ•Šμ•˜μœΌλ©΄ λ‹€μ‹œ wait()둜 λŒμ•„κ°„λ‹€ β†’ μ•ˆμ „ μ‹€νŒ¨ 방지

반볡문 λ°–μ—μ„œ μ ˆλŒ€ wait()λ₯Ό ν˜ΈμΆœν•˜μ§€ 말라.

μƒˆλ‘œμš΄ μ½”λ“œμ—μ„œλŠ” wait와 notify λŒ€μ‹  λ™μ‹œμ„± μœ ν‹Έλ¦¬ν‹°λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μ’‹λ‹€. ν•˜μ§€λ§Œ λ ˆκ±°μ‹œ μ½”λ“œμ—μ„œ μ‚¬μš©ν•  μˆ˜λ°–μ— μ—†λ‹€λ©΄ λ‹€μŒκ³Ό 같이 μ‚¬μš©ν•΄μ•Ό ν•œλ‹€:

synchronized (obj) {
    while (쑰건이 μΆ©μ‘±λ˜μ§€ μ•Šμ•˜λ‹€) {
        obj.wait(); // ν˜„μž¬ μŠ€λ ˆλ“œλ₯Ό λŒ€κΈ° μƒνƒœλ‘œ μ „ν™˜
    }
    // 쑰건이 μΆ©μ‘±λ˜μ—ˆμ„ λ•Œ μˆ˜ν–‰ν•  λ™μž‘
}

2. 쑰건이 μΆ©μ‘±λ˜μ§€ μ•Šμ•„λ„ κΉ¨μ–΄λ‚  수 μžˆλŠ” 상황듀

  1. λ‹€λ₯Έ μŠ€λ ˆλ“œμ˜ μƒνƒœ λ³€κ²½
    • notify() 호좜 ν›„ λ‹€λ₯Έ μŠ€λ ˆλ“œκ°€ 락을 μ–»μ–΄ μƒνƒœλ₯Ό λ³€κ²½ν•  수 μžˆλ‹€.
  2. μ‹€μˆ˜ λ˜λŠ” μ•…μ˜μ  notify 호좜
    • 외뢀에 λ…ΈμΆœλœ 객체λ₯Ό 락으둜 μ‚¬μš©ν•˜λ©΄ λ‹€λ₯Έ μŠ€λ ˆλ“œκ°€ notifyλ₯Ό μ‹€μˆ˜λ‘œ ν˜ΈμΆœν•  수 μžˆλ‹€.
  3. ν—ˆμœ„ 각성(Spurious Wakeup)
    • notifyκ°€ ν˜ΈμΆœλ˜μ§€ μ•Šμ•˜λŠ”λ°λ„ μŠ€λ ˆλ“œκ°€ κΉ¨μ–΄λ‚˜λŠ” ν˜„μƒ. μ΄λŠ” λ“œλ¬Όμ§€λ§Œ λ°œμƒν•  수 μžˆλ‹€. μŠ€λ ˆλ“œκ°€ notify 없이도 κΉ¨μ–΄λ‚˜λŠ” ν˜„μƒμ„ ν—ˆμœ„ 각성이라고 ν•œλ‹€. 이λ₯Ό λ°©μ§€ν•˜λ €λ©΄ waitλ₯Ό λ°˜λ“œμ‹œ 반볡문 λ‚΄λΆ€μ—μ„œ ν˜ΈμΆœν•΄μ•Ό ν•œλ‹€.
  4. κ³Όλ„ν•œ notifyAll 호좜
    • 쑰건이 μΆ©μ‘±λ˜μ§€ μ•Šμ€ μŠ€λ ˆλ“œκΉŒμ§€ λͺ¨λ‘ κΉ¨μ–΄λ‚  수 μžˆλ‹€.
    • ν•˜μ§€λ§Œ κΉ¨μ–΄λ‚œ μŠ€λ ˆλ“œκ°€ 쑰건을 κ²€μ‚¬ν•˜κ³  μΆ©μ‘±λ˜μ§€ μ•ŠμœΌλ©΄ λ‹€μ‹œ λŒ€κΈ°ν•˜κ²Œ λ˜λ―€λ‘œ ν”„λ‘œκ·Έλž¨μ˜ 정확성은 μœ μ§€λœλ‹€.

3. notify와 notifyAll 쀑 무엇을 μ‚¬μš©ν•΄μ•Ό ν•˜λŠ”κ°€?

일반 원칙

  • 항상 notifyAll()을 μ‚¬μš©ν•˜λΌ.
    • λͺ¨λ“  λŒ€κΈ° 쀑인 μŠ€λ ˆλ“œκ°€ κΉ¨μ–΄λ‚˜ 쑰건을 ν™•μΈν•˜κ²Œ λ˜λ―€λ‘œ μ •ν™•ν•œ κ²°κ³Όλ₯Ό 보μž₯ν•œλ‹€.
    • 일뢀 μŠ€λ ˆλ“œκ°€ λΆˆν•„μš”ν•˜κ²Œ κΉ¨μ–΄λ‚˜λ”λΌλ„ μ΄λŠ” μ„±λŠ₯ 문제일 뿐 ν”„λ‘œκ·Έλž¨μ˜ 정확성에 영ν–₯을 λ―ΈμΉ˜μ§€ μ•ŠλŠ”λ‹€.

notify μ‚¬μš© μ‹œ μ£Όμ˜μ‚¬ν•­

  • 단 ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œλ§Œ 쑰건을 μΆ©μ‘±ν•  λ•Œ μ΅œμ ν™”λ₯Ό μœ„ν•΄ notify()λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.
  • ν•˜μ§€λ§Œ, λ‹€μŒκ³Ό 같은 μœ„ν—˜μ΄ μžˆλ‹€:
    • λ‹€λ₯Έ μŠ€λ ˆλ“œκ°€ μ‹€μˆ˜λ‘œ μ€‘μš”ν•œ notifyλ₯Ό μ‚ΌμΌœλ²„λ¦¬λ©΄ ν•„μˆ˜μ μœΌλ‘œ κΉ¨μ–΄λ‚˜μ•Ό ν•  μŠ€λ ˆλ“œκ°€ μ˜μ›νžˆ λŒ€κΈ° μƒνƒœμ— 빠진닀.
    • μ΄λŠ” 외뢀에 곡개된 객체에 λŒ€ν•΄ μ•…μ˜μ μœΌλ‘œ waitλ₯Ό ν˜ΈμΆœν•˜λŠ” 경우 λ”μš± 치λͺ…적이닀.

4. notify와 notifyAll의 차이점

  • notify: λŒ€κΈ° 쀑인 μŠ€λ ˆλ“œ 쀑 ν•˜λ‚˜λ§Œ κΉ¨μš΄λ‹€.
  • notifyAll: λͺ¨λ“  λŒ€κΈ° 쀑인 μŠ€λ ˆλ“œλ₯Ό κΉ¨μš΄λ‹€.

μ•ˆμ „ν•œ 선택: 일반적으둜 notifyAll을 μ‚¬μš©ν•˜λŠ” 것이 더 μ•ˆμ „ν•˜λ‹€. 쑰건이 λ§Œμ‘±λ˜μ§€ μ•Šμ€ μŠ€λ ˆλ“œλ“€μ€ λ‹€μ‹œ λŒ€κΈ° μƒνƒœλ‘œ λŒμ•„κ°€λ―€λ‘œ λ¬Έμ œκ°€ μ—†λ‹€.

5. 핡심정리

  • wait와 notifyλŠ” λ™μ‹œμ„±μ˜ μ–΄μ…ˆλΈ”λ¦¬ 언어에 λΉ„μœ λœλ‹€.
    • java.util.concurrentλŠ” κ³ μˆ˜μ€€ μ–Έμ–΄λ‘œ, 더 μ•ˆμ „ν•˜κ³  κ°•λ ₯ν•œ λ™μ‹œμ„± μœ ν‹Έλ¦¬ν‹°λ₯Ό μ œκ³΅ν•œλ‹€.
  • μƒˆλ‘œμš΄ μ½”λ“œμ—μ„œλŠ” wait와 notifyλ₯Ό μ‚¬μš©ν•  ν•„μš”κ°€ 거의 μ—†λ‹€.
  • λ ˆκ±°μ‹œ μ½”λ“œ μœ μ§€λ³΄μˆ˜ μ‹œ:
    • wait()λŠ” λ°˜λ“œμ‹œ while λ¬Έ μ•ˆμ—μ„œ ν˜ΈμΆœν•˜λΌ.
    • notifyAll()을 μ‚¬μš©ν•˜λΌ. notifyλŠ” 극히 μ‘°μ‹¬ν•΄μ„œ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

πŸ“š κ²°λ‘ 

java.util.concurrent νŒ¨ν‚€μ§€λŠ” λ³΅μž‘ν•œ λ™μ‹œμ„± 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•œ κ³ μˆ˜μ€€ μœ ν‹Έλ¦¬ν‹°λ₯Ό μ œκ³΅ν•œλ‹€.

  1. λ™μ‹œμ„± μ»¬λ ‰μ…˜: ConcurrentHashMapκ³Ό 같은 μ»¬λ ‰μ…˜μ€ 높은 λ™μ‹œμ„±μ„ μ œκ³΅ν•˜λ©° μ™ΈλΆ€ 동기화가 ν•„μš” μ—†λ‹€.
  2. 동기화 μž₯치: CountDownLatch, Semaphore, Phaser 등을 톡해 μŠ€λ ˆλ“œ κ°„ ν˜‘λ ₯ 및 μž‘μ—… μ‘°μœ¨μ„ ν•  수 μžˆλ‹€.
  3. λ ˆκ±°μ‹œ μ½”λ“œ: wait와 notifyλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•œλ‹€λ©΄ λ°˜λ“œμ‹œ 반볡문과 ν•¨κ»˜ μ‚¬μš©ν•˜λ©° notifyAll을 ꢌμž₯ν•œλ‹€.

μƒˆλ‘œμš΄ μ½”λ“œμ—μ„œλŠ” λ™μ‹œμ„± μœ ν‹Έλ¦¬ν‹°λ₯Ό 적극 ν™œμš©ν•˜μ—¬ μ•ˆμ „ν•˜κ³  효율적인 λ™μ‹œμ„± ν”„λ‘œκ·Έλž˜λ°μ„ κ΅¬ν˜„ν•˜μž.

μ°Έκ³ 
- https://jjingho.tistory.com/131