🌱 오늘의 주제 : 쓰레드의 동기화 & wait()과 notify()
🌱 쓰레드의 동기화 (synchronization)
- 멀티쓰레드 프로스세의 경우, 쓰레드 A가 작업하던 도중에 다른 쓰레드 B가 임의로 변경하였다면, 다시 쓰레드 A가 제어권을 받아서 나머지 작업을 마쳤을 때 원래 의도했던 것과는 다른 결과를 얻을 수 있다.
- 이러한 일을 방지하기 위해서 한 쓰레드가 특정 작업을 끝마치기 전까지 다른 쓰레드에 의해 방해받지 않도록 하는 것이 필요한데, 이 개념이 바로 '임계 영역'과 '잠금'이다.
- 한 쓰레드가 진행중인 작업을 다른 쓰레드가 간섭하지 못하게 막는 것을 쓰레드의 동기화라고 한다.
1. 메서드 전체를 임계 영역으로 지정
public synchronized void calcSum() {
..
}
2. 특정한 영역을 임계 영역으로 지정
synchronized(객체의 참조변수) {
..
}
package chapter13;
public class Chapter13_12 {
public static void main(String[] args) {
// synchronized를 이용한 동기화
Runnable r = new RunnableEx12();
new Thread(r).start(); // ThreadGroup에 의해 참조되므로 gc대상이 아니다?
new Thread(r).start(); // ThreadGroup에 의해 참조되므로 gc대상이 아니다?
}
}
class Account {
private int balance = 1000;
public int getBalance() { // 읽을때도 synchronized를 붙여야 음수 결과 안나온다. // private으로 해야 동기화 의미가 있다.
return balance;
}
public synchronized void withdraw(int money) { // synchronized를 붙이면 한 쓰레드가 자물쇠를 갖고 있는 것이기 때문에 '음수'의 결과가 안 나온다.
if (balance >= money) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
balance -= money;
}
}
}
class RunnableEx12 implements Runnable {
Account acc = new Account();
public void run() {
while (acc.getBalance() > 0) {
// 100, 200, 300 중의 한 값을 임으로 선택해서 출금(withdraw)
int money = (int) (Math.random() * 3 + 1) * 100;
acc.withdraw(money);
System.out.println("balance: " + acc.getBalance());
}
}
}
-----
balance: 900
balance: 700
balance: 500
balance: 400
balance: 100
balance: 0
balance: 0
🌱 wait()과 notify()
- 특정 쓰레드가 객체의 락을 가진 상태로 오랜 시간을 보내지 않도록 하는 것이 중요하다. 만일 계좌에 출금 돈이 부족해서 한 쓰레드가 락을 돈이 입금될 때까지 오랜 시간을 보낸다면, 다른 쓰레드들은 모두 해당 객체의 락을 기다리느라 다른 작업들도 원활히 진행되지 않을 것이다.
- 이러한 상황을 고려하기 위한 것이 wait()와 notify()이다. 일단 wait()을 호출하여 쓰레드가 락을 반납하고 기다리게 한다. 나중에 작업을 진행 할 수 있는 상황이 되면 nofity()를 호출해서, 다시 락을 얻어 작업을 진행 할 수 있게하는 것이다.
- wait가 호출되면, 실행 중이던 쓰레드는 해당 객체의 대기실(waiting pool)에서 통지를 기다린다. notify()가 호출되면, 해당 객체의 대기실에 있던 모든 쓰레드 중에서 임의의 쓰레드만 통지를 받는다. notifyAll()은 기다리고 있는 모든 쓰레드에게 통보를 하지만, 그래도 lock을 얻을 수 있는 것은 하나의 쓰레드이다.
- waiting pool은 객체마다 존재한다. 그러므로, notfiyAll()이 호출된다고 해서 모든 객체의 waiting pool의 쓰레드가 깨어지는 것이 아니라, 호출된 객체의 대기중인 쓰레드만 해당된다.
wait(), notify(), notifyAll()
- Object에 정의 되어 있다.
- 동기화 블록내에서만 사용 할 수 있다.
- 보다 효율적인 동기화를 가능하게 한다.
'Java' 카테고리의 다른 글
Java - 람다식 (0) | 2023.03.27 |
---|---|
Java - BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); (0) | 2023.03.23 |
Java - 쓰레드의 상태 (0) | 2023.03.23 |
Java - 쓰레드의 우선순위 & 데몬 쓰레드 (0) | 2023.03.21 |
Java - 싱글쓰레드와 멀티쓰레드 & 쓰레드의 I/O 블락킹(blocking) (0) | 2023.03.20 |