13.9.1 동기화
- 한 쓰레드가 진행 중인 작업을 다른 쓰레드가 간섭하지 못하도록 막는 것
- 임계 영역(critical section), 락(lock)
- 락을 획득한 쓰레드만이 임계 영역 내부에 접근 가능
13.9.2 동기화 방법
- 메서드 전체를 임계 영역으로 지정
public synchronized void calcSum(){ //임계 영역 }
- 특정 영역을 임계 영역으로 지정(메서드 내부)
synchronized(객체의 참조변수){ //임계 영역 }
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
Runnable r = new RunnableEx22();
new Thread(r).start();
new Thread(r).start();
}
}
class Account {
private int balance = 1000; // private으로 해야 동기화가 의미가 있다.
public int getBalance() {
return balance;
}
public synchronized void withdraw(int money){ // synchronized로 메서드를 동기화
if(balance >= money) {
try { Thread.sleep(1000);} catch(InterruptedException e) {}
balance -= money;
}
}
}
class RunnableEx22 implements Runnable {
Account acc = new Account();
public void run() {
while(acc.getBalance() > 0) {
int money = (int)(Math.random() * 3 + 1) * 100;
acc.withdraw(money);
System.out.println("balance:"+acc.getBalance());
}
}
}
//실행결과(실행마다 결과가 다름)
balance:700
balance:500
balance:200
balance:200
balance:100
balance:100
balance:100
balance:100
balance:100
balance:100
balance:100
balance:100
balance:0
balance:0
13.9.3 wait()과 notify()
- wait() : 임계 영역 내부에서 코드를 수행하다 작업 진행이 불가능해 지면 락을 반납하고 waiting pool에서 대기
- notify() : 작업을 중단한 쓰레드가 다시 락을 얻어서 작업 수행
- 메서드
- Object에 정의되어 있음
- 동기화 블록내에서만 사용가능
- 효율적인 동기화를 가능하게 함
void wait()
void wait(long timeout)
void wait(long timeout, int nanos)
void notify()
void notifyAll()
13.9.4 기아 현상과 경쟁 상태
- 기아 현상 : 오랫동안 waiting pool에서 대기하는 상태
- 경쟁 상태 : 여러 쓰레드가 lock을 얻기 위해 경쟁하는 상태
13.9.5 Lock 클래스의 종류
- ReenterantLock : 재진입이 가능한 lock
- ReenterantReadWriteLock : 읽기에는 공유, 쓰기에는 배타 lock
- StampedLock : ReenterantReadWriteLock + 낙관적인 lock 추가
long stamp = lock.tryOptimisticRead();
13.9.6 ReenterantLock
- 가장 오래 기다린 쓰레드에 lock 할당
- 생성자
ReenterantLock()
ReenterantLock(boolean fair)
- 가장 오래 기다린 쓰레드를 확인하기 위해서 성능이 떨어짐
- 수동으로 lock을 잠그고 해제해야 함
- 메서드
void lock()
void unlock()
boolean inLocked()
boolean tryLock()
boolean tryLock(long timeout, TimeUnit unit) throw InterruptedException
- 임계영역 내부에서 에러가 발생할 수 있으므로 에러처리를 해 주는것이 좋음
ReenterantLock lock = new ReenterantLock();
lock.lock();
try{
//임계 영역
} finally{
lock.unlock();
}
- tryLock은 lock이 걸려져 있을 시 lock을 얻기위해 기다리지 않는다.(사용자에게 작업처리에 대한 의사를 물을 때 사용하면 좋음)
13.9.7 ReenterantLock과 Condition
- 쓰레드를 공유 객체의 waiting pool에 몰아넣는 대신, 구분이 필요한 쓰레드를 각각의 waiting pool에서 따로 기다리게 함(wait()메서드를 사용하면 객체 각각의 waiting pool에서 기다림)
- Condition 생성
ReenterantLock lock = new ReenterantLock();
Condition forCook = lock.newContidion();
Condition forCust = lock.newContidion();
- await() : 실행 → 일시정지
- signal() : 일시정지 → 실행
forCook.await(); //wait()
forCust.signal() //notify()
'자바의 정석 정리' 카테고리의 다른 글
자바의 정석 - 13.11 fork & join 프레임워크 (0) | 2022.08.29 |
---|---|
자바의 정석 - 13.10 volatile (0) | 2022.08.29 |
자바의 정석 - 13.8 쓰레드 실행제어 (0) | 2022.08.29 |
자바의 정석 - 13.7 데몬 쓰레드(Daemon Thread) (0) | 2022.08.29 |
자바의 정석 - 13.6 쓰레드 그룹(Thread Group) (0) | 2022.08.29 |