Chapter 11 λ™μ‹œμ„±

λ™μ‹œμ„± ν”„λ‘œκ·Έλž˜λ°μ€ 단일 μŠ€λ ˆλ“œ ν”„λ‘œκ·Έλž˜λ°λ³΄λ‹€ μ–΄λ ΅λ‹€.

잘λͺ»λ  수 μžˆλŠ” 일이 λŠ˜μ–΄λ‚˜κ³  문제λ₯Ό μž¬ν˜„ν•˜κΈ°λ„ μ–΄λ ΅κΈ° λ•Œλ¬Έμ΄λ‹€. ν•˜μ§€λ§Œ ν˜„μž¬μ˜ λ©€ν‹°μ½”μ–΄ ν”„λ‘œμ„ΈμŠ€λ₯Ό 잘 ν™œμš©ν•˜λ €λ©΄ λ°˜λ“œμ‹œ μ΅ν˜€μ•Ό ν•˜λŠ” κΈ°μˆ μ΄λ‹€.

Item 78 κ³΅μœ μ€‘μΈ κ°€λ³€ λ°μ΄ν„°λŠ” 동기화해 μ‚¬μš©ν•˜λΌ

μ‚¬μš©μ€‘μΈ μŠ€λ ˆλ“œλ₯Ό λ©ˆμΆœλ•Œ Thread.stop λ©”μ„œλ“œλŠ” μ‚¬μš©ν•˜μ§€ 말자.

λ‹€μŒμ€ μŠ€λ ˆλ“œλ₯Ό λ©ˆμΆ”λŠ” μ˜¬λ°”λ₯Έ 방법이닀.

  1. μžμ‹ μ˜ boolean ν•„λ“œλ₯Ό ν΄λ§ν•˜λ©΄μ„œ κ·Έ 값이 true κ°€ 되면 λ©ˆμΆ˜λ‹€.
  2. 이 boolean ν•„λ“œλ₯Ό false 둜 μ΄ˆκΈ°ν™” 해놓고 λ‹€λ₯Έ μŠ€λ ˆλ“œμ—μ„œ 이 μŠ€λ ˆλ“œλ₯Ό λ©ˆμΆ”κ³ μž ν• λ•Œ true 둜 λ³€κ²½ν•œλ‹€.

boolean ν•„λ“œλ₯Ό 읽고 μ“°λŠ” μž‘μ—…μ€ μ›μžμ μ΄λ‹€.

μ•„λž˜ μ½”λ“œλŠ” λ¬΄ν•œν•˜κ²Œ μ‹€ν–‰λ˜λŠ” μ½”λ“œμ΄λ‹€.

public class StopThread {
  private static boolean stopRequest;

  public static void main(String[] args) throws InterruptedException {
    Thread backgroundThread = new Thread(() -> {
      int i = 0;
      while(!stopRequest)
        i++;
    });
    backgroundThread.start();

    TimeUnit.SECONDS.sleep(1);
    stopRequested = true;
  }
}

μœ„ μ½”λ“œμ—μ„œ stopRequest ν•„λ“œμ˜ 읽기와 μ“°κΈ° μž‘μ—…μ‹œ 동기화해 μ ‘κ·Όν•΄μ•Ό ν•œλ‹€.




Β 
Β 
Β 

Β 
Β 
Β 




Β 





Β 



public class StopThread {
  private static boolean stopRequest;

  private static synchronized void requestStop() {
    stopRequested = true;
  }

  private static synchronized boolean stopRequested() {
    return stopRequested;
  }

  public static void main(String[] args) throws InterruptedException {
    Thread backgroundThread = new Thread(() -> {
      int i = 0;
      while(!stopRequested())
        i++;
    });
    backgroundThread.start();

    TimeUnit.SECONDS.sleep(1);
    requestStop();
  }
}

읽기와 μ“°κΈ°κ°€ λͺ¨λ‘ 동기화 λ˜μ§€ μ•ŠμœΌλ©΄ λ™μž‘μ„ 보μž₯ν•˜μ§€ μ•ŠλŠ”λ‹€.

volatile ν•œμ •μžλŠ” 베타적 μˆ˜ν–‰κ³ΌλŠ” 관계 μ—†μ§€λ§Œ 항상 κ°€μž₯ μ΅œκ·Όμ— 기둝된 값을 읽도둝 보μž₯ν•œλ‹€.


Β 













public class StopThread {
  private static volatile boolean stopRequest;

  public static void main(String[] args) throws InterruptedException {
    Thread backgroundThread = new Thread(() -> {
      int i = 0;
      while (!stopRequested) i++;
    });
    backgroundThread.start();

    TimeUnit.SECONDS.sleep(1);
    stopRequested = true;
  }
}

InterruptedException

InterruptedException λŠ” ν˜„μž¬ 싀행쀑인 μŠ€λ ˆμ΄λ“œμ—μ„œ Thread.interrupt() λ₯Ό ν˜ΈμΆœν•œ λˆ„κ΅°κ°€λ₯Ό μ˜λ―Έν•œλ‹€.

ν•΄λ‹Ή μ΄λ²€νŠΈκ°€ μΌμ–΄λ‚œλ‹€λ©΄ μ•„λž˜μ™€ 같이 μ΅œλŒ€ν•œ 빨리 μ’…λ£Œν•΄μ•Ό ν•œλ‹€.

try {
  Thread.sleep(10000);
} catch (InterruptedException e) {
  Thread.currentThread().interrupt();
  throw new AssertionError(e);
}

μœ„ μ½”λ“œλŠ” throw 되기 μ΄μ „μ˜ μŠ€λ ˆλ“œ μΈν„°λŸ½νŠΈ μƒνƒœλ₯Ό λ¨Όμ € λ³΅μ›ν•œ λ‹€μŒμ— κΈ°λ³Έ invariant κ°€ μœ„λ°˜λ˜μ—ˆμŒμ„ λ‚˜νƒ€λ‚΄λŠ” AssertionError 을 throw ν•œλ‹€.

https://riptutorial.com/ko/java/example/2116/interruptedexception의-처리

κ°€μž₯ 쒋은 방법은 κ°€λ³€ λ°μ΄ν„°λŠ” 단일 μŠ€λ ˆλ“œμ—μ„œλ§Œ μ‚¬μš©ν•˜λ„λ‘ ν•˜λ©° λΆˆλ³€ λ°μ΄ν„°λ§Œ κ³΅μœ ν•˜κ±°λ‚˜ 아무것도 κ³΅μœ ν•΄μ„œλŠ” μ•ˆλœλ‹€.

Item 79 κ³Όλ„ν•œ λ™κΈ°ν™”λŠ” 피해라

κ³Όλ„ν•œ λ™κΈ°ν™”λŠ” μ„±λŠ₯을 λ–¨μ–΄λœ¨λ¦¬κ³ , κ΅μ°©μƒνƒœμ— 빠뜨리며 심지어 예츑 λΆˆκ°€λŠ₯ν•œ λ™μž‘μ„ λ‚¨κΈΈμˆ˜λ„ μžˆλ‹€.

응닡 λΆˆκ°€ ν˜Ήμ€ μ•ˆμ „ μ‹€νŒ¨ (Safe Failure) 을 ν”Όν•˜λ €λ©΄ 동기화 λ©”μ„œλ“œλ‚˜ 동기화 λΈ”λŸ­ μ•ˆμ„ μ œμ–΄λ₯Ό ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ μ–‘λ„ν•˜λ©΄ μ•ˆλœλ‹€.

  • 동기화 μ˜μ—­ μ•ˆμ—λŠ” μž¬μ •μ˜ ν•  μˆ˜μžˆλŠ” λ©”μ„œλ“œλŠ” ν˜ΈμΆœν•˜λ©΄ μ•ˆλœλ‹€.
  • ν΄λΌμ΄μ–ΈνŠΈκ°€ λ„˜κ²¨μ€€ ν•¨μˆ˜ 객체λ₯Ό ν˜ΈμΆœν•΄μ„œλ„ μ•ˆλœλ‹€.

μ™ΈλΆ€μ˜ λ©”μ„œλ“œλŠ” μ–΄λ– ν•œ 일을 ν•  수 μžˆλŠ”μ§€ 보μž₯ν•˜κΈ° μ–΄λ €μ›Œ μ˜ˆμ™Έ, κ΅μ°©μƒνƒœ, λ°μ΄ν„°μ˜ 훼손을 μ•ΌκΈ°μ‹œν‚¬μˆ˜λ„ μžˆλ‹€.

λ•Œλ¬Έμ— 동기화 μ˜μ—­μ—μ„œλŠ” κ°€λŠ₯ν•œ 일을 적게 ν•˜λŠ”κ²ƒμ΄λ‹€.

κ°€λ³€ 클래슀λ₯Ό μž‘μ„±ν•˜λ €κ±°λ“  λ‹€μŒ λ‘κ°€μ§€μ˜ 방법이 μžˆλ‹€.

  • 동기화λ₯Ό μ „ν˜€ ν•˜μ§€ 말고 κ·Έ 클래슀λ₯Ό λ™μ‹œμ— μ‚¬μš©ν•˜λŠ” ν΄λž˜μŠ€κ°€ μ™ΈλΆ€μ—μ„œ μ•Œμ•„μ„œ 동기화 ν•˜κ²Œ λ§Œλ“€μž
  • 동기화λ₯Ό λ‚΄λΆ€μ—μ„œ μˆ˜ν–‰ν•΄ Thread Safe ν•œ 클래슀둜 λ§Œλ“€μž

StringBuffer μΈμŠ€ν„΄μŠ€λŠ” 단일 μŠ€λ ˆλ“œ μž„μ—λ„ λΆˆκ΅¬ν•˜κ³  λ‚΄λΆ€μ μœΌλ‘œ 동기화λ₯Ό μˆ˜ν–‰ν•œλ‹€
λ•Œλ¬Έμ— StringBuilder λ₯Ό ν†΅ν•˜μ—¬ 동기화λ₯Ό μ œκ±°ν•˜μ˜€λ‹€. (StringBuffer - 동기화 = StringBuilder)

java.util.Random 도 이와 λ™μΌν•œ 이유둜 java.util.concurrent.ThreadLocalRandom 으둜 λŒ€μ²΄ λ˜μ—ˆλ‹€.

Item 80 μŠ€λ ˆλ“œλ³΄λ‹€λŠ” μ‹€ν–‰μž, νƒœμŠ€ν¬, μŠ€νŠΈλ¦Όμ„ μ• μš©ν•˜λΌ.

java.util.concurrent 의 νŒ¨ν‚€μ§€λŠ” μ‹€ν–‰μž ν”„λ ˆμž„μ›Œν¬ (Executor Framework) λΌκ³ ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€ 기반의 μœ μ—°ν•œ νƒœμŠ€ν¬ μ‹€ν–‰ κΈ°λŠ₯을 λ‹΄κ³  μžˆλ‹€.

// μ‹€ν–‰μž 생성
ExecutorService exec = Executors.newSingleThreadExecutor();

// μ‹€ν–‰μžμ— νƒœμŠ€ν¬λ₯Ό λ„˜κΉ€
exec.execute(runnable);

// μ‹€ν–‰μž μ’…λ£Œ
exec.shutdown();

λŒ€λΆ€λΆ„μ˜ λ™μ‹œμ„± ν”„λ‘œκ·Έλž˜λ°μ„ μ΄μš©μ‹œ java.util.concurrent νŒ¨ν‚€μ§€ λ‚΄μ—μ„œ 도움을 λ°›μ„μˆ˜ μžˆλ‹€.

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

java.util.concurrent 의 κ³ μˆ˜μ€€ μœ ν‹Έλ¦¬ν‹°λŠ” 세가지 λ²”μ£Όλ‘œ λ‚˜λˆŒμˆ˜ μžˆλ‹€.

  • μ‹€ν–‰μž ν”„λ ˆμž„μ›Œν¬ (Executor Framework)
  • λ™μ‹œμ„± μ»¬λ ‰μ…˜ (Concurrent Collection)
  • 동기화 μž₯치 (Synchronizer)

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

높은 λ™μ‹œμ„±μ— λ„λ‹¬ν•˜κΈ° μœ„ν•΄ 동기화λ₯Ό 각각 λ‚΄λΆ€μ—μ„œ μˆ˜ν–‰ν•œλ‹€.

λ”°λΌμ„œ λ™μ‹œμ„± μ»¬λ ‰μ…˜μ—μ„œ λ™μ‹œμ„±μ„ 무λ ₯ν™”ν•˜λŠ”κ²ƒμ€ λΆˆκ°€λŠ₯ν•˜λ©°, μ™ΈλΆ€μ—μ„œ 락을 μΆ”κ°€λ‘œ μ‚¬μš©ν•˜λ©΄ 였히렀 속도가 λŠλ €μ§„λ‹€.

Java SE 8 μ—μ„œλŠ” μΈν„°νŽ˜μ΄μŠ€μ— λ””ν΄νŠΈ λ©”μ„œλ“œκ°€ μΆ”κ°€λ˜μ—ˆλŠ”λ° Map 의 pushIfAbsent(key, value) κ°€ λŒ€ν‘œμ μΈ μ˜ˆμΈλ‹€.

pushIfAbsent(key, value) λŠ” 주어진 킀에 λ§€ν•‘λœ 값이 아직 μ—†μ„λ•Œλ§Œ 집어 λ„£λŠ”λ‹€.
κΈ°μ‘΄ 값이 μžˆμ—‡λ‹€λ©΄ κ·Έ 값을 λ°˜ν™˜ν•˜κ³  μ—†μ—ˆλ‹€λ©΄ null 을 λ°˜ν™˜ν•œλ‹€.

μ•„λž˜ μ½”λ“œλŠ” ConcurrentMap 으둜 κ΅¬ν˜„ν•œ λ™μ‹œμ„± μ •κ·œν™” 맡이닀.

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

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

μœ„ μ½”λ“œμ—μ„œ ConcurrentHashMap 의 get() 검색 κΈ°λŠ₯이 μ΅œμ ν™” λ˜μ–΄ μžˆμœΌλ―€λ‘œ λ¨Όμ € get 을 ν˜ΈμΆœν•˜μ—¬ putIfAbsent 을 ν˜ΈμΆœν•˜λ©΄ 더 λΉ λ₯΄λ‹€.


Β 








public static String intern(String str) {
  String result = map.get(str);
  if (result == null) {
    result = map.putIfAbsent(str, str);
    if (result == null)
      result = str;
  }
  return result;
}

λ™κΈ°ν™”ν•œ μ»¬λŸ­μ„Ό 맡보닀 λ™μ‹œμ„± μ»¬λ ‰μ…˜μ΄ μ„±λŠ₯적으둜 더 μš°μˆ˜ν•˜λ‹€.
즉 Collections.synchronizedMap 보닀 ConcurrentHashMap 을 μ‚¬μš©ν•˜λŠ”κ²ƒμ΄ 훨씬 μ’‹λ‹€.

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

Item 82 μŠ€λ ˆλ“œ μ•ˆμ „μ„± μˆ˜μ€€μ„ λ¬Έμ„œν™” ν•˜λΌ

κ΅¬ν˜„ λ©”μ„œλ“œμ— synchronized ν•œμ •μžκ°€ ν¬ν•¨λ˜μ–΄ μžˆλ‹€κ³  해도 λ©”μ„œλ“œ κ΅¬ν˜„ν•˜λŠ” κ³Όμ •μ—μ„œ μ‚¬μš©ν• λΏμ΄λ©° ν•΄λ‹Ή κΈ°λŠ₯을 보μž₯ν•  수 μ—†λ‹€.

λ•Œλ¬Έμ— λ©€ν‹°μŠ€λ ˆλ“œ ν™˜κ²½μ—μ„œ APIλ₯Ό μ•ˆμ „ν•˜κ²Œ μ‚¬μš©ν•˜λ €λ©΄ ν΄λž˜μŠ€κ°€ μ§€μ›ν•˜λŠ” μŠ€λ ˆλ“œ μ•ˆμ „μ„± μˆ˜μ€€μ„ μ •ν™•νžˆ λͺ…μ‹œν•΄μ•Ό ν•œλ‹€.

Collections.synchronizedMap 에 μ‚¬μš©λ˜λŠ” API λ¬Έμ„œλ₯Ό 참고해보면 λ‹€μŒκ³Ό 같이 μ¨μž‡λ‹€.

// synchronizedMap 이 λ°˜ν™˜ν•œ 맡의 μ»¬λ ‰μ…˜ λ·°λ₯Ό μˆœνšŒν•˜λ €λ©΄ λ°˜λ“œμ‹œ κ·Έ 맡을 락으둜 μ‚¬μš©ν•΄ μˆ˜λ™μœΌλ‘œ λ™κΈ°ν™”ν•˜λΌ

Map<K, V> m = Collections.synchronizedMap(new HashMap<>());
Set<K, V> s = m.keySet();
...
synchronized(m) {
  for (K key: s) {
    key.f();
  }
}

// μ΄λŒ€λ‘œ λ”°λ₯΄μ§€ μ•ŠμœΌλ©΄ λ™μž‘μ„ μ˜ˆμΈ‘ν•  수 μ—†λ‹€.

μŠ€λ ˆλ“œμ˜ μ•ˆμ •μ„±μ„ 보톡 클래슀의 λ¬Έμ„œν™” 주석에 κΈ°μž¬ν•˜μ§€λ§Œ, νŠΉμ • λ©”μ„œλ“œμ— ν•œν•΄μ„œλŠ” ν•΄λ‹Ή λ©”μ„œλ“œ 주석에 κΈ°μž¬ν•˜λ„λ‘ ν•˜μž.

Item 83 ν”„λ‘œκ·Έλž¨μ˜ λ™μž‘μ„ μŠ€λ ˆλ“œ μŠ€μΌ€μ€„λŸ¬μ— κΈ°λŒ€μ§€ 말라

μ—¬λŸ¬ μŠ€λ ˆλ“œκ°€ μ‹€ν–‰ 쀑이면 운영체제의 μŠ€λ ˆλ“œ μŠ€μΌ€μ€„λŸ¬κ°€ μ–΄λ–€ μŠ€λ ˆλ“œλ₯Ό μ–Όλ§ˆλ‚˜ 였래 싀행할지 μ •ν•œλ‹€.

정상적인 운영 체제라면 이 μž‘μ—…μ„ κ³΅μ •ν•˜κ²Œ μˆ˜ν–‰ν•˜μ§€λ§Œ μŠ€μΌ€μ€„λ§ 정책은 μš΄μ˜μ²΄μ œλ§ˆλ‹€ λ‹€λ₯΄λ©° 잘 μž‘μ„±λœ ν”„λ‘œκ·Έλž¨μ΄λΌλ©΄ 이 κΈ°μ€€ 정책에 μ˜μ‘΄ν•΄μ„œλŠ” μ•ˆλœλ‹€.

μ •ν™•μ„±μ΄λ‚˜ μ„±λŠ₯이 μŠ€λ ˆλ“œ μŠ€μΌ€μ€„λŸ¬μ— 따라 λ‹¬λΌμ§€λŠ” ν”„λ‘œκ·Έλž¨μ΄λΌλ©΄ λ‹€λ₯Έ ν”Œλž«νΌμ— μ΄μ‹ν•˜κΈ° μ–΄λ ΅λ‹€.