From f5bbd92baa306eedda21bd4dc7b4b8745a0c44b0 Mon Sep 17 00:00:00 2001 From: gengminy Date: Sun, 17 Dec 2023 21:49:11 +0900 Subject: [PATCH] =?UTF-8?q?post:=20[9=EC=A3=BC=EC=B0=A8=5F=EB=85=B8?= =?UTF-8?q?=EA=B2=BD=EB=AF=BC]=208.=20Thread=20&=20Lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...2\262\275\353\257\274-8.-Thread-&-Lock.md" | 238 ++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 "_posts/coding-interview-univ/8.-Thread-&-Lock/2023-12-17-\353\205\270\352\262\275\353\257\274-8.-Thread-&-Lock.md" diff --git "a/_posts/coding-interview-univ/8.-Thread-&-Lock/2023-12-17-\353\205\270\352\262\275\353\257\274-8.-Thread-&-Lock.md" "b/_posts/coding-interview-univ/8.-Thread-&-Lock/2023-12-17-\353\205\270\352\262\275\353\257\274-8.-Thread-&-Lock.md" new file mode 100644 index 0000000..d260e12 --- /dev/null +++ "b/_posts/coding-interview-univ/8.-Thread-&-Lock/2023-12-17-\353\205\270\352\262\275\353\257\274-8.-Thread-&-Lock.md" @@ -0,0 +1,238 @@ +--- +title: 🦊 8. Thread & Lock +author: gengminy +date: 2023-12-17 12:00:00 +09:00 +categories: [μ½”λ”© 인터뷰 λŒ€ν•™, 8. Thread & Lock] +tags: [μ½”λ”© 인터뷰 λŒ€ν•™, 운영체제, 9μ£Όμ°¨, λ…Έκ²½λ―Ό] +render_with_liquid: false +math: true +--- + +# μžλ°”μ˜ μŠ€λ ˆλ“œ + +μžλ°”μ˜ λͺ¨λ“  μŠ€λ ˆλ“œλŠ” `java.lang.Thread` 클래슀 객체에 μ˜ν•΄ μƒμ„±λ˜κ³  μ œμ–΄λœλ‹€. + +μžλ°”μ—μ„œ μŠ€λ ˆλ“œλ₯Ό κ΅¬ν˜„ν•˜λŠ” 방법은 두 가지가 μžˆλ‹€. + +- `java.lang.Runnable` μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜κΈ° +- `java.lang.Thread` 클래슀λ₯Ό 상속받기 + +## Runnable μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ” 방법 + +```java +public interface Runnable { + void run(); +} +``` + +- Runnable μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ” 클래슀λ₯Ό λ§Œλ“ λ‹€ +- Thread νƒ€μž…μ˜ 객체 μƒμ„±μžμ— Runnable 객체λ₯Ό 인자둜 λ„˜κΈ΄λ‹€ +- Thread 객체의 start() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•œλ‹€ + +```java +public static void main(String[] args) { + RunnableThreadExample instance = new RunnableThreadExample(); + Thread thread = new Thread(instance); + thread.start(); + + while (instance.count != 5) { + try { + Thread.sleep(25); + } catch (InterruptedException exc) { + exc.printStackTrace(); + } + } +} +``` + +## Thread 클래슀 상속 + +```java +public class ThreadExample extends Thread { + int count = 0; + + public void run() { + ... + } +} + +public class ExampleB { + public static void main(String args[]) { + ThreadExample instance = new ThreadExample(); + instance.start(); + + while (instance.count != 5) { + try { + Thread.sleep(250); + } catch (InterruptedException exc) { + exc.printStackTrace(); + } + } + } +} +``` + +μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ” λŒ€μ‹  Thread 클래슀λ₯Ό 상속받아 μΈμŠ€ν„΄μŠ€ν™”ν•˜κ³  start() λ₯Ό 직접 ν˜ΈμΆœν•˜κ²Œ λœλ‹€. + +### Thread 상속 vs. Runnable μΈν„°νŽ˜μ΄μŠ€ κ΅¬ν˜„ + +μŠ€λ ˆλ“œλ₯Ό 생성할 λ•Œ Runnable μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ” 것이 더 μ„ ν˜Έλœλ‹€. + +- μžλ°”λŠ” 닀쀑 상속을 μ§€μ›ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ—, Thread λ₯Ό μƒμ†ν•˜κ²Œ 되면 ν•˜μœ„ ν΄λž˜μŠ€λŠ” λ‹€λ₯Έ 클래슀λ₯Ό 상속할 수 μ—†λ‹€. 반만 Runnable μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ” ν΄λž˜μŠ€λŠ” λ‹€λ₯Έ 클래슀λ₯Ό 상속받을 수 μžˆλ‹€. +- Thread 클래슀의 λͺ¨λ“  것을 μƒμ†λ°›λŠ” 것이 λ„ˆλ¬΄ λΆ€λ‹΄μŠ€λŸ¬μš΄ κ²½μš°μ— Runnable 을 κ΅¬ν˜„ν•˜λŠ” 편이 λ‚«λ‹€. + +## 동기화와 락 + +μ–΄λ–€ ν”„λ‘œμ„ΈμŠ€ μ•ˆμ—μ„œ μƒμ„±λœ μŠ€λ ˆλ“œλ“€μ€ 같은 λ©”λͺ¨λ¦¬ 곡간을 κ³΅μœ ν•œλ‹€. + +두 μŠ€λ ˆλ“œκ°€ 같은 μžμ›μ„ λ™μ‹œμ— λ³€κ²½ν•˜λŠ” κ²½μš°μ—λŠ” λ¬Έμ œκ°€ λœλ‹€. + +μžλ°”λŠ” 곡유 μžμ›μ— λŒ€ν•œ 접근을 μ œμ–΄ν•˜κΈ° μœ„ν•œ 동기화 방법을 μ œκ³΅ν•œλ‹€. + +### λ™κΈ°ν™”λœ λ©”μ„œλ“œ + +`synchronized` ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•  λ•Œ 곡유 μžμ›μ— λŒ€ν•œ 접근을 μ œμ–΄ν•œλ‹€. + +λ©”μ„œλ“œ λ˜λŠ” νŠΉμ • μ½”λ“œ 블둝에 μ μš©ν•  수 μžˆλ‹€. + +```java +public class MyObject { + public synchronized void foo(String name) { + ... + } +} +``` + +두 개의 MyClass μΈμŠ€ν„΄μŠ€κ°€ foo λ₯Ό λ™μ‹œμ— ν˜ΈμΆœν•  수 μžˆμ„κΉŒ? + +같은 MyObject μΈμŠ€ν„΄μŠ€λ₯Ό 가리킀고 μžˆλ‹€λ©΄ λ™μ‹œ 호좜이 λΆˆκ°€λŠ₯ν•˜λ‹€. + +λ‹€λ₯Έ μΈμŠ€ν„΄μŠ€λ₯Ό 가지고 μžˆλ‹€λ©΄ κ°€λŠ₯ν•˜λ‹€. + +```java +MyObject obj1 = new MyObject(); +MyObject obj1 = new MyObject(); +MyClass thread1 = new MyClass(obj1, "1"); +MyClass thread1 = new MyClass(obj2, "2"); +thread1.start(); +thread2.start(); +// λ™μ‹œ 호좜 OK + +MyObject obj = new MyObject(); +MyClass thread1 = new MyClass(obj, "1"); +MyClass thread1 = new MyClass(obj, "2"); +thread1.start(); +thread2.start(); +// λ™μ‹œ 호좜 X, λ‹€λ₯Έ ν•˜λ‚˜λŠ” 기닀리고 μžˆμ–΄μ•Ό ν•œλ‹€. +``` + +정적 λ©”μ„œλ“œλŠ” 클래슀 락에 μ˜ν•΄ λ™κΈ°ν™”λœλ‹€. + +같은 ν΄λž˜μŠ€μ— μžˆλŠ” λ™κΈ°ν™”λœ 정적 λ©”μ„œλ“œλŠ” 두 μŠ€λ ˆλ“œμ—μ„œ λ™μ‹œμ— 싀행될 수 μ—†λ‹€. + +ν•˜λ‚˜λŠ” foo λ₯Ό ν˜ΈμΆœν•˜κ³ , λ‹€λ₯Έ ν•˜λ‚˜λŠ” bar을 ν˜ΈμΆœν•œλ‹€κ³  해도 말이닀. + +```java +public class MyClass extends Thread { + ... + public void run() { + if (name.equlas("1")) MyObject.foo(name); + else if (name.equals("2")) MyObject.bar(name); + } +} + +public class MyObejct { + public static synchronized void foo(String name) { ... } + public static synchronized void bar(String name) { ... } +} +``` + +```java +// μ‹€ν–‰κ²°κ³Ό +Thread 1.foo(): starting + +Thread 1.bar(): ending + +Thread 2.foo(): starting + +Thread 2.bar(): ending +``` + +## λ™κΈ°ν™”λœ 블둝 + +νŠΉμ •ν•œ μ½”λ“œ 블둝을 동기화할 수 μžˆλ‹€. μ΄λŠ” λ©”μ„œλ“œλ₯Ό λ™κΈ°ν™”ν•˜λŠ” 것과 μ•„μ£Ό λΉ„μŠ·ν•˜κ²Œ λ™μž‘ν•œλ‹€. + +```java +public class MyClass extends Thread { + ... + public void run() { + myObj.foo(name); + } +} + +public class MyObject { + ... + public void foo(String name) { + synchronized(this) { + ... + } + } +} + +``` + +MyObject μΈμŠ€ν„΄μŠ€ ν•˜λ‚˜λ‹Ή ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œλ§Œμ΄ synchronized 블둝 μ•ˆμ˜ μ½”λ“œλ₯Ό μ‹€ν–‰ν•  수 μžˆλ‹€. + +# 락 + +μ’€ 더 μ„Έλ°€ν•˜κ²Œ 동기화λ₯Ό μ œμ–΄ν•˜κ³  μ‹Άλ‹€λ©΄ 락(Lock)을 μ‚¬μš©ν•œλ‹€. + +락(λ˜λŠ” Monitor)을 곡유 μžμ›μ— 뢙이면 ν•΄λ‹Ή μžμ›μ— λŒ€ν•œ 접근을 동기화할 수 μžˆλ‹€. + +μŠ€λ ˆλ“œκ°€ ν•΄λ‹Ή μžμ›μ— μ ‘κ·Όν•˜λ €λ©΄ κ·Έ μžμ›μ— λΆ™μ–΄μžˆλŠ” 락을 νšλ“ν•΄μ•Ό ν•œλ‹€. + +μ–΄λ–€ μžμ›μ΄ ν”„λ‘œκ·Έλž¨ λ‚΄μ˜ μ΄κ³³μ €κ³³μ—μ„œ μ‚¬μš©λ˜μ§€λ§Œ ν•œ λ²ˆμ— ν•œ μŠ€λ ˆλ“œλ§Œ μ‚¬μš©ν•˜λ„λ‘ λ§Œλ“€κ³ μž ν•  λ•Œ 주둜 락을 μ΄μš©ν•œλ‹€. + +```java +public class LockedATM { + private Lock lock; + private int balance = 100; + + public LockedATM() { + lock = new ReentrantLock(); + } + + public int withdraw(int value) { + lock.lock(); + ... + lock.unlock(); + return value; + } + + public int deposit(int value) { + lock.lock(); + ... + lock.unlock(); + return value; + } +} +``` + +락을 μ‚¬μš©ν•˜λ©΄ 곡유된 μžμ›μ΄ 예기치 μ•Šκ²Œ λ³€κ²½λ˜λŠ” 일을 막을 수 μžˆλ‹€. + +## κ΅μ°©μƒνƒœμ™€ κ΅μ°©μƒνƒœ 방지 + +κ΅μ°©μƒνƒœ(deadlock)λž€, μŠ€λ ˆλ“œ1은 μŠ€λ ˆλ“œ2κ°€ λ“€κ³  μžˆλŠ” 객체의 락이 풀리기λ₯Ό 기닀리고 있고 μŠ€λ ˆλ“œ2λŠ” μŠ€λ ˆλ“œ1이 λ“€κ³ μžˆλŠ” 객체의 락이 풀리기λ₯Ό κΈ°λ‹€λ¦¬λŠ” 상황이닀. + +λͺ¨λ“  μŠ€λ ˆλ“œκ°€ 락이 풀리기λ₯Ό 기닀리고 있기 λ•Œλ¬Έμ— λ¬΄ν•œ λŒ€κΈ° μƒνƒœμ— 빠진닀. + +κ΅μ°©μƒνƒœκ°€ λ°œμƒν•˜λ €λ©΄ 4가지 쑰건을 λͺ¨λ‘ μΆ©μ‘±λ˜μ–΄μ•Ό ν•œλ‹€. + +- μƒν˜Έλ°°μ œ(mutual exclusion): ν•œ λ²ˆμ— ν•œ ν”„λ‘œμ„ΈμŠ€λ§Œ 곡유 μžμ›μ„ μ‚¬μš©ν•  수 μžˆλ‹€. +- λ“€κ³  기닀리기(hold and wait): 곡유 μžμ›μ— λŒ€ν•œ μ ‘κ·Ό κΆŒν•œμ„ 가진 ν”„λ‘œμ„ΈμŠ€κ°€ μ ‘κ·Ό κΆŒν•œμ„ μ–‘λ³΄ν•˜μ§€ μ•Šμ€ μƒνƒœμ—μ„œ λ‹€λ₯Έ μžμ›μ— λŒ€ν•œ μ ‘κ·Ό κΆŒν•œμ„ μš”κ΅¬ν•  수 μžˆλ‹€. +- μ„ μ·¨ λΆˆκ°€λŠ₯(preemption): ν•œ ν”„λ‘œμ„ΈμŠ€κ°€ λ‹€λ₯Έ ν”„λ‘œμ„ΈμŠ€μ˜ μžμ› μ ‘κ·Ό κΆŒν•œμ„ κ°•μ œλ‘œ μ·¨μ†Œν•  수 μ—†λ‹€. +- λŒ€κΈ° μƒνƒœμ˜ 사이클(circular wait): 두 개 μ΄μƒμ˜ ν”„λ‘œμ„ΈμŠ€κ°€ μžμ› 접근을 κΈ°λ‹€λ¦¬λŠ”λ°, κ·Έ 관계에 사이클이 μ‘΄μž¬ν•œλ‹€. + +κ΅μ°©μƒνƒœλ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ 이 쑰건 κ°€μš΄λ° ν•˜λ‚˜λ₯Ό μ œκ±°ν•˜λ©΄ λœλ‹€. + +λŒ€λΆ€λΆ„μ˜ κ΅μ°©μƒνƒœ 방지 μ•Œκ³ λ¦¬μ¦˜μ€ 4번 쑰건을 λ§‰λŠ”λ° 초점이 λ§žμΆ°μ Έμžˆλ‹€.