[JAVA] μ“°λ ˆλ“œ 동기화

μ“°λ ˆλ“œ λ™κΈ°ν™”λž€?


μ‹±κΈ€μ“°λ ˆλ“œ ν”„λ‘œμ„ΈμŠ€λŠ” ν•˜λ‚˜μ˜ 메인 μ“°λ ˆλ“œλ§Œ μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” ν”„λ‘œμ„ΈμŠ€λ₯Ό λœ»ν•˜λ©°, λ©€ν‹°μ“°λ ˆλ“œ ν”„λ‘œμ„ΈμŠ€λŠ” 메인 μ“°λ ˆλ“œ 포함 두 개 μ΄μƒμ˜ μ“°λ ˆλ“œκ°€ λ™μ‹œμ— μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” ν”„λ‘œμ„ΈμŠ€λ₯Ό λœ»ν•©λ‹ˆλ‹€. 

 

κ·Έλ ‡κΈ° λ•Œλ¬Έμ— μ‹±κΈ€μ“°λ ˆλ“œ ν”„λ‘œμ„ΈμŠ€μ˜ 경우 ν”„λ‘œμ„ΈμŠ€μ˜ μžμ›μ„ 가지고 μž‘μ—…ν•˜λŠ” 큰 λ¬Έμ œκ°€ μ—†μ§€λ§Œ, λ©€ν‹°μ“°λ ˆλ“œ ν”„λ‘œμ„ΈμŠ€ 같은 경우 μ—¬λŸ¬ μ“°λ ˆλ“œκ°€ 같은 ν”„λ‘œμ„ΈμŠ€ λ‚΄μ˜ μžμ›μ„ κ³΅μœ ν•΄μ„œ μž‘μ—…ν•˜κΈ° λ•Œλ¬Έμ— μ„œλ‘œμ˜ μž‘μ—…μ— 영ν–₯을 쀄 수 밖에 μ—†μŠ΅λ‹ˆλ‹€. λ§Œμ•½μ— μ“°λ ˆλ“œ Aκ°€ ν•˜λ‚˜μ˜ 곡유 μžμ›μ„ 가지고 μž‘μ—…μ„ μˆ˜ν–‰ν•˜λ‹€κ°€ μ“°λ ˆλ“œ Bμ—κ²Œ μ œμ–΄κΆŒμ΄ λ„˜μ–΄κ°”μ„ λ•Œ, μ“°λ ˆλ“œ Aκ°€ μž‘μ—…ν•˜λ˜ 곡유 μžμ›μ„ μ“°λ ˆλ“œBκ°€ μž„μ˜λ‘œ κ±΄λ“œλ Έλ‹€λ©΄ μ“°λ ˆλ“œAλŠ” μ›ν•˜λ˜ κ²°κ³Όλ₯Ό 얻을 수 없을 κ²ƒμž…λ‹ˆλ‹€. 

 

μ΄λŸ¬ν•œ 일을 λ°©μ§€ν•˜κΈ° μœ„ν•΄μ„œλŠ” ν•œ μ“°λ ˆλ“œκ°€ νŠΉμ • μž‘μ—…μ„ 끝마치기 μ „κΉŒμ§€ λ‹€λ₯Έ μ“°λ ˆλ“œμ— μ˜ν•΄ 방해받지 μ•Šλ„λ‘ ν•΄μ•Όν•˜λ©°, κ·Έλž˜μ„œ λ„μž…λœ κ°œλ…μ΄ μž„κ³„μ˜μ—­(Critical Section)κ³Ό 잠금(Lock) μž…λ‹ˆλ‹€. 즉, 곡유 데이터λ₯Ό μ‚¬μš©ν•˜λŠ” μ½”λ“œ μ˜μ—­μ„ μž„κ³„μ˜μ—­μœΌλ‘œ μ§€μ •ν•˜κ³  곡유 데이터가 가지고 μžˆλŠ” Lock을 νšλ“ν•œ ν•˜λ‚˜μ˜ μ“°λ ˆλ“œλ§Œ μ ‘κ·Όν•  수 μžˆλ„λ‘ ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. 이처럼 ν•œ μ“°λ ˆλ“œκ°€ 진행쀑인 μž‘μ—…μ„ λ‹€λ₯Έ μ“°λ ˆλ“œκ°€ κ°„μ„­ν•˜μ§€ λͺ»ν•˜λ„둝 ν•˜λŠ” 것을 'μ“°λ ˆλ“œμ˜ 동기화' 라고 ν•©λ‹ˆλ‹€.

 

 

synchronized ν‚€μ›Œλ“œμ™€ λ©”μ„œλ“œ 


κ°€μž₯ κ°„λ‹¨ν•œ 동기화 방법은 synchronized ν‚€μ›Œλ“œλ₯Ό μ΄μš©ν•œ 동기화 λ°©λ²•μž…λ‹ˆλ‹€. 이 ν‚€μ›Œλ“œλŠ” μž„κ³„μ˜μ—­μ„ μ„€μ •ν•˜λŠ”λ° μ‚¬μš©λ©λ‹ˆλ‹€. λ°©λ²•μ—λŠ” 두가지가 μžˆλŠ”λ°, λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

 

1. λ©”μ„œλ“œ 전체λ₯Ό μž„κ³„μ˜μ—­μœΌλ‘œ μ„€μ •

public synchronized void Method() {}

ν•΄λ‹Ή λ°©λ²•μœΌλ‘œ μ§„ν–‰ν•˜κ²Œ 되면 λ©”μ„œλ“œ 전체가 μž„κ³„μ˜μ—­μœΌλ‘œ μ„€μ •λ©λ‹ˆλ‹€. μ“°λ ˆλ“œλŠ” synchronized λ©”μ„œλ“œκ°€ 호좜된 μ‹œμ λΆ€ν„° ν•΄λ‹Ή λ©”μ„œλ“œκ°€ ν¬ν•¨λœ 객체의 lock을 μ–»μ–΄ μ§„ν–‰ν•˜λ‹€κ°€ λ©”μ„œλ“œκ°€ μ’…λ£Œλ˜λ©΄ lock을 λ°˜λ‚©ν•˜κ²Œ λ©λ‹ˆλ‹€. 

 

2. 일뢀 μ˜μ—­λ§Œμ„ μž„κ³„μ˜μ—­μœΌλ‘œ μ„€μ •

synchronized(객체) {}  

ν•΄λ‹Ή λ°©λ²•μœΌλ‘œ μ§„ν–‰ν•˜κ²Œ 되면 λ©”μ„œλ“œ 전체가 μ•„λ‹Œ, λ©”μ„œλ“œμ˜ νŠΉμ • 뢀뢄을 μž„κ³„μ˜μ—­μœΌλ‘œ μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λ•Œ μ°Έμ‘°λ³€μˆ˜λŠ” 락을 κ±Έκ³ μžν•˜λŠ” 객체λ₯Ό μ°Έμ‘°ν•˜λŠ” 것이여야 ν•©λ‹ˆλ‹€. (참고둜 객체당 ν•˜λ‚˜μ˜ 락을 κ°€μ§€κ²Œ λ©λ‹ˆλ‹€.) κ΄„ν˜Έλ‘œ 감싸고 μžˆλŠ” 뢀뢄이 synchronized블둝 μ˜μ—­μ΄λ©°, ν•΄λ‹Ή μ˜μ—­μœΌλ‘œ λ“€μ–΄κ°€λ©΄μ„œ μ§€μ •λœ 객체의 lock을 μ–»κ²Œλ˜κ³  ν•΄λ‹Ή μ˜μ—­μ„ λ²—μ–΄λ‚  λ•Œ lock을 λ°˜λ‚©ν•˜κ²Œ λ©λ‹ˆλ‹€. 

 

두 방법 λͺ¨λ‘ lock νšλ“ λ°˜λ‚©μ΄ μžλ™μœΌλ‘œ μ΄λ£¨μ–΄μ§€λ―€λ‘œ κ°œλ°œμžλŠ” λ‹¨μˆœνžˆ μž„κ³„ μ˜μ—­λ§Œ μ„€μ •ν•΄μ£Όλ©΄ λ©λ‹ˆλ‹€. μž„κ³„μ˜μ—­μ€ λ©€ν‹°μ“°λ ˆλ“œ ν”„λ‘œκ·Έλž¨μ˜ μ„±λŠ₯을 μ’Œμš°ν•˜κΈ° λ•Œλ¬Έμ— κ°€λŠ₯ν•˜λ©΄ λ©”μ„œλ“œ 전체에 락을 κ±°λŠ” 것보닀, synchronizedλΈ”λŸ­μœΌλ‘œ μž„κ³„μ˜μ—­μ„ μ΅œμ†Œν™”ν•˜μ—¬ 효율적인 ν”„λ‘œκ·Έλž¨μ΄ λ˜λ„λ‘ λ…Έλ ₯ν•΄μ•Ό ν•©λ‹ˆλ‹€.

 

 

wait(), notify()


synchronized둜 λ™κΈ°ν™”ν•˜μ—¬ 곡유 데이터λ₯Ό λ³΄ν˜Έν•˜λŠ” 것도 μ€‘μš”ν•˜μ§€λ§Œ, νŠΉμ • μ“°λ ˆλ“œκ°€ 객체의 락을 가진 μƒνƒœλ‘œ 였래 μ‹œκ°„μ„ 보내지 μ•Šλ„λ‘ ν•˜λŠ” 것도 ꡉμž₯히 μ€‘μš”ν•©λ‹ˆλ‹€. κ·Έλž˜μ„œ κ³ μ•ˆλœ 것이 λ°”λ‘œ wait() κ³Ό notify()μž…λ‹ˆλ‹€. λ™κΈ°ν™”λœ μž„κ³„μ˜μ—­μ˜ μ½”λ“œλ₯Ό μˆ˜ν–‰ν•˜λ‹€κ°€ μž‘μ—…μ„ μˆ˜ν–‰ν•  상황이 μ•„λ‹ˆλΌλ©΄, wait()을 ν˜ΈμΆœν•˜μ—¬ μ“°λ ˆλ“œκ°€ 락을 λ°˜λ‚©ν•˜κ³  λŒ€κΈ°μƒνƒœλ‘œ λŒμž…ν•˜κ²Œ λ©λ‹ˆλ‹€. 그러면 λ‹€λ₯Έ μ“°λ ˆλ“œκ°€ 락을 μ–»μ–΄ ν•΄λ‹Ή 객체에 λŒ€ν•œ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 있게 λ©λ‹ˆλ‹€. κ·Έ 이후에 μž‘μ—…μ„ μˆ˜ν–‰ν•  상황이 되면 notify()λ₯Ό ν˜ΈμΆœν•˜μ—¬, μž‘μ—…μ„ μ€‘λ‹¨ν–ˆλ˜ μ“°λ ˆλ“œλ₯Ό κΉ¨μ›Œ λ‹€μ‹œ 락을 μ–»μ–΄ μž‘μ—…μ„ 진행할 수 있게 ν•©λ‹ˆλ‹€. 

 

ν•˜μ§€λ§Œ notify()λ₯Ό μ΄μš©ν•˜λ©΄ 였래 κΈ°λ‹€λ¦° μ“°λ ˆλ“œκ°€ 락을 μ–»λŠ”λ‹€λŠ” 보μž₯은 μ—†μŠ΅λ‹ˆλ‹€. wait()이 호좜되면 μ‹€ν–‰ μ€‘μ΄λ˜ μ“°λ ˆλ“œλŠ” waiting poolμ—μ„œ 톡지λ₯Ό κΈ°λ‹€λ¦¬κ²Œ λ˜λŠ”λ°, notify()κ°€ 호좜되면 ν•΄λ‹Ή 객체의 waiting pool쀑에 μž„μ˜μ˜ μ“°λ ˆλ“œλ§Œ 톡지λ₯Ό λ°›κ²Œ λ©λ‹ˆλ‹€. λ¬Όλ‘  notifyAll()을 μ΄μš©ν•˜λ©΄ λͺ¨λ“  μ“°λ ˆλ“œλ₯Ό 깨울 수 μžˆμ§€λ§Œ κ²°κ΅­ 락은 객체당 ν•˜λ‚˜μ΄κΈ° λ•Œλ¬Έμ— μž„μ˜μ˜ μ“°λ ˆλ“œλ§Œ μ‹€ν–‰λ˜κ³  λ‚˜λ¨Έμ§€ μ“°λ ˆλ“œλ“€μ€ λ‹€μ‹œ waiting poolμ—μ„œ κΈ°λ‹€λ¦¬κ²Œ λ©λ‹ˆλ‹€.

 

  • κΈ°μ•„ ν˜„μƒ
notify()λ₯Ό ν™œμš©ν•˜λ©΄ μœ„μ—μ„œ λ§ν–ˆλ“―μ΄ μž„μ˜μ˜ μ“°λ ˆλ“œλ§Œ κΉ¨μ–΄λ‚˜κ²Œ 되며, μ–΄μ©Œλ©΄ μ΅œμ•…μ˜ μƒν™©μ—μ„œ μ›ν•˜λŠ” μ“°λ ˆλ“œκ°€ λκΉŒμ§€ κΉ¨μ–΄λ‚˜μ§€ λͺ»ν•˜λŠ” μƒνƒœκ°€ μ˜¬μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ 상황을 κΈ°μ•„ ν˜„μƒμ΄λΌκ³  ν•©λ‹ˆλ‹€. 이 ν˜„μƒμ„ λ§‰μœΌλ €λ©΄ notify() λŒ€μ‹  notifyAll()을 ν™œμš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. ν•˜μ§€λ§Œ notifyAll()도 λΆ„λͺ…νžˆ λ¬Έμ œκ°€ μ‘΄μž¬ν•©λ‹ˆλ‹€.

 

  • 경쟁 μƒνƒœ
notifyAll()을 ν™œμš©ν•˜λ©΄ λͺ¨λ“  μ“°λ ˆλ“œλ₯Ό 깨울 수 μžˆμ§€λ§Œ, κ²°κ΅­ 객체당 락은 ν•˜λ‚˜μ΄κΈ° λ•Œλ¬Έμ— 락 ν•˜λ‚˜λ₯Ό 두고 λͺ¨λ“  μ“°λ ˆλ“œκ°€ μ„œλ‘œ κ²½μŸν•˜κ²Œ λ©λ‹ˆλ‹€. μ΄λŸ¬ν•œ 것을 경쟁 μƒνƒœλΌκ³  ν•©λ‹ˆλ‹€. 이 경쟁 μƒνƒœλ₯Ό κ°œμ„ ν•˜κΈ° μœ„ν•΄μ„œλŠ” 결ꡭ에 μžμ‹ μ΄ μ›ν•˜λŠ” μ“°λ ˆλ“œλ₯Ό 직접 지λͺ…ν•˜μ—¬ 톡지해야 ν•©λ‹ˆλ‹€.

 

Lock


동기화할 수 μžˆλŠ” 방법은 synchronizedλΈ”λŸ­ 외에도 'java.util.concurrent.locks'νŒ¨ν‚€μ§€κ°€ μ œκ³΅ν•˜λŠ” locksν΄λž˜μŠ€λ“€μ„ μ΄μš©ν•˜λŠ” 방법이 μžˆμŠ΅λ‹ˆλ‹€. synchronizedλΈ”λŸ­μ„ μ‚¬μš©ν•˜κ²Œ 되면 μžλ™μœΌλ‘œ lock이 걸리고 풀리기 λ•Œλ¬Έμ— ꡉμž₯히 νŽΈλ¦¬ν•©λ‹ˆλ‹€. 심지어 synchronizedλΈ”λŸ­ μ˜μ—­μ—μ„œ μ˜ˆμ™Έκ°€ λ°œμƒν•œλ‹€ν•΄λ„ lock은 μžλ™μ μœΌλ‘œ ν’€λ¦½λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ λ•Œλ‘œλŠ” 같은 λ©”μ„œλ“œ λ‚΄μ—μ„œλ§Œ lock을 κ±Έ 수 μžˆλ‹€λŠ” μ œμ•½μ΄ λΆˆνŽΈν•˜κΈ°λ„ ν•©λ‹ˆλ‹€. λ”°λΌμ„œ 이런 μ œμ•½μ΄ λ¬Έμ œκ°€ 될 λ•Œ lock클래슀λ₯Ό μ‚¬μš©ν•˜κ²Œ λ©λ‹ˆλ‹€. 

 

lock클래슀의 μ’…λ₯˜λŠ” λ‹€μŒκ³Ό 같이 3가지 μ’…λ₯˜κ°€ μžˆμŠ΅λ‹ˆλ‹€. 

 

  • 1. ReentrantLock
ReentrantLock은 κ°€μž₯ 일반적인 Lockμž…λ‹ˆλ‹€. reentrant(μž¬μ§„μž…ν•  수 μžˆλŠ”)μ΄λΌλŠ” 단어가 μ•žμ— 뢙은 μ΄μœ λŠ” μœ„μ—μ„œ μ–ΈκΈ‰ν–ˆλ˜ wait(), notify() 처럼 νŠΉμ • μ‘°κ±΄μ—μ„œ Lock을 ν”Œκ³ , λ‚˜μ€‘μ— λ‹€μ‹œ Lock을 μ–»μ–΄μ„œ Critical Section으둜 λ“€μ–΄μ™€μ„œ μ΄ν›„μ˜ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 있기 λ•Œλ¬Έμž…λ‹ˆλ‹€. 

 

  • ReentrantReadWriteLock
ReentrantReadWriteLock은 읽기λ₯Ό μœ„ν•œ Lockκ³Ό μ“°κΈ°λ₯Ό μœ„ν•œ Lock을 λ”°λ‘œ μ œκ³΅ν•©λ‹ˆλ‹€. ReentrantReadWriteLock은 읽기 Lock이 걸렀있으면, λ‹€λ₯Έ μ“°λ ˆλ“œκ°€ 읽기 Lock을 μ€‘λ³΅ν•΄μ„œ κ±Έκ³  읽기λ₯Ό μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ 읽기 Lock이 κ±Έλ¦° μƒνƒœμ—μ„œ μ“°κΈ° Lock을 κ±°λŠ” 것은 ν—ˆμš©ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λ˜ν•œ λ°˜λŒ€μ˜ κ²½μš°λ„ λ˜ν•œ λ§ˆμ°¬κ°€μ§€ μž…λ‹ˆλ‹€. 

 

  • StampedLock
StampedLock은 Lock을 κ±Έκ±°λ‚˜ 해지할 λ•Œ 'μŠ€νƒ¬ν”„(longνƒ€μž…μ˜ μ •μˆ˜κ°’)'을 μ‚¬μš©ν•˜λ©°, 읽기와 μ“°κΈ°λ₯Ό μœ„ν•œ lock외에 '낙관적 읽기 lock'이 μΆ”κ°€λœ κ²ƒμž…λ‹ˆλ‹€.  λ§Œμ•½ 읽기 lock이 κ±Έλ €μžˆλ‹€λ©΄ μ“°κΈ° lock을 κ±ΈκΈ° μœ„ν•΄μ„œλŠ” 읽기 lock이 풀릴 λ•Œ κΉŒμ§€ κΈ°λ‹€λ €μ•Όν•˜λŠ”λ°μ— λΉ„ν•΄ '낙관적 읽기 lock'은 읽기 lock이 κ±Έλ €μžˆλŠ” μƒνƒœμ—μ„œ μ“°κΈ° lock을 건닀면, 읽기 lock은 λ°”λ‘œ ν’€λ¦¬κ²Œ λ©λ‹ˆλ‹€. λ”°λΌμ„œ 무쑰건 읽기 lock을 걸지 μ•Šκ³ , 읽기 lockκ³Ό μ“°κΈ° lock이 μΆ©λŒν•  λ•Œλ§Œ μ“°κΈ°κ°€ λλ‚œ 후에 읽기 lock을 κ±Έμ–΄μ•Όλ§Œ ν•©λ‹ˆλ‹€.

 

Condition


μœ„μ—μ„œ wait()κ³Ό notify()λ₯Ό μ‚¬μš©ν•˜λ©΄ μžμ‹ μ΄ μ›ν•˜λŠ” μ“°λ ˆλ“œλ₯Ό 직접 κ³¨λΌμ„œ ν†΅μ§€ν•˜μ§€ λͺ»ν•œλ‹€λŠ” 단점을 μ–ΈκΈ‰ν–ˆμ—ˆμŠ΅λ‹ˆλ‹€. Condition은 이 λ¬Έμ œμ μ„ ν•΄κ²°ν•˜κΈ° μœ„ν•΄ κ³ μ•ˆλœ κ²ƒμž…λ‹ˆλ‹€. wait()κ³Ό notify()둜 곡유 객체의 waiting pool에 같이 λͺ°μ•„λ„£λŠ” λŒ€μ‹ , 각각 μ’…λ₯˜μ˜ μ“°λ ˆλ“œλ₯Ό μœ„ν•œ Condition을 λ§Œλ“€μ–΄μ„œ 각각의 waiting poolμ—μ„œ λ”°λ‘œ 기닀리도둝 ν•˜λ©΄ λ¬Έμ œλŠ” 해결될 κ²ƒμž…λ‹ˆλ‹€. 즉, λ§Œμ•½μ— μŒμ‹μ„ μƒμ„±ν•˜λŠ” μš”λ¦¬μ‚¬ μ“°λ ˆλ“œμ™€ μŒμ‹μ„ μ†ŒλΉ„ν•˜λŠ” μ†λ‹˜ μ“°λ ˆλ“œκ°€ μžˆλ‹€κ³  ν•΄λ΄…μ‹œλ‹€.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public class ThreadRestaurant {
    /* μƒλž΅ */
}
 
class Customer implements Runnable {
    @Override
    public void run() {
        /* μƒλž΅ */
    }
}
 
class Cook implements Runnable {
    @Override
    public void run() {
        /* μƒλž΅ */
    }
}
 
class Table {
    String[] dishNames = {"donut""donut""burger"};
    final int MAX_FOOD = 2// μ΅œλŒ€ 2개 
    private ArrayList<String> dishes = new ArrayList<>(); 
    
    private ReentrantLock lock = new ReentrantLock(); //일반적인 Lock 
    private Condition forCook = lock.newCondition(); //μš”λ¦¬μ‚¬μš© condition
    private Condition forCus = lock.newCondition(); //μ†λ‹˜μš© condition
    public void add(String dish) {
        lock.lock(); //락을 κ±΄λ‹€.
            try {
                while(dishes.size() >= MAX_FOOD) { //μŒμ‹μ„ λͺ»λ§Œλ“œλŠ” μƒν™© 
                    String name = Thread.currentThread().getName();
                    System.out.println(name + " is waiting");
                    try {forCook.await();Thread.sleep(500);} catch(InterruptedException e) {} //wait!
                }
                dishes.add(dish);
                forCus.signal();
                System.out.println("Dishes :" + dishes.toString());
            } finally {
                lock.unlock(); //μ–΄λ–€ μΌμ΄ μΌμ–΄λ‚˜λ“  lock을 λ°˜ν™˜ν•΄μ•Όν•¨.
            }
    }
    
    public void remove(String dishName) {
        lock.lock(); //락을 κ±΄λ‹€.
            try {
                while(dishes.size()==0) { //μŒμ‹μ΄ μ—†μœΌλ©΄ λ¨Ήμ„ μˆ˜ μ—†λ‹€.
                    String name = Thread.currentThread().getName(); 
                    System.out.println(name+ " is waiting.");
                    try {forCus.await(); Thread.sleep(500);} catch(InterruptedException e) {} //wait!
                }
 
                for(int i=0; i<dishes.size(); i++) { //νŠΉμ •μŒμ‹μ„ μ œκ±° 
                    if(dishName.contentEquals(dishes.get(i))) {
                        dishes.remove(i);
                        forCook.signal();
                        return;
                    }
                }
            
            } finally {
                lock.unlock();
            }
    }
    
    public int dishNum() { return dishNames.length; }
}
 
 
 

 

μœ„μ— Table Class κ΅¬ν˜„ 뢀뢄을 보면 각각 μ†λ‹˜μš© Conditionκ³Ό μš”λ¦¬μ‚¬μš© Condition을 μ„ μ–Έν•˜λŠ” 뢀뢄이 μ‘΄μž¬ν•©λ‹ˆλ‹€. μœ„μ˜ μ˜ˆμ‹œμ²˜λŸΌ ꡬ뢄이 ν•„μš”ν•œ μ“°λ ˆλ“œ λ³„λ‘œ Condition을 μ„ μ–Έν•˜μ—¬ μ‚¬μš©ν•˜κ²Œ 되면 notify()와 wait()이 λΆˆκ°€λŠ₯ν–ˆλ˜ 뢀뢄을 μ†μ‹œμ›ν•˜κ²Œ ν•΄κ²°ν•  수 있게 λ©λ‹ˆλ‹€. 참고둜 Condition ν΄λž˜μŠ€λŠ” notify(), wait()이 μ•„λ‹Œ signal(), await() μž…λ‹ˆλ‹€.