위로
아래
임계영역과 동기화
임계영역 (critical section)
- 한 번에 하나씩만 처리
- 다수의 스레드가 공유 자원을 참조하는 코드 영역
스레드 동기화 (synchronization)
- 다수의 스레드가 공유 자원을 충돌 없이 사용할 수 있게 돕는다.
- 공유 자원에 배타적이고 독점적으로 접근할 수 있는 방법
- 임계영역을 동기화하려고 제공하는 기능
- 스레드가 synchronized로 지정된 동기화 블록에 진입하면 락(Lock)을 걸고, 그 블록을 벗어날 때 락을 푼다
- 동기화 블록에 진입한 스레드가 코드를 실행하는 동안, 다른 스레드는 락이 풀릴 때까지 모두 중지시킨다.
주의점
- 모든 스레드가 공정하게 공유자원을 사용할 수 있도록 하지 않으면 다음 문제들이 발생할 수 있다.
- 기아 (starvation) : 다른 작업을 수행하지 못한 채 하나 이상의 스레드가 공유 자원을 얻으려고 계속 대기하는 상태
- 교착 (deadlock) : 2개 이상의 스레드가 서로 어떤 작업을 수행해 주기를 기다리는 상태
임계영역을 동기화시키는 방법
- 메소드에 synchronized 지정
- 코드의 일부에 synchronized 지정
예시
더보기
public class CarThread extends Thread {
private String who;
private SharedCar car;
private String where;
public CarThread(String who, SharedCar car, String where) {
this.who = who;
this.car = car;
this.where = where;
}
public void run() {
car.drive(who, where);
}
}
import java.util.Random;
public class SharedCar {
public synchronized void drive(String name, String where) {
System.out.println(name + "님이 자동차에 탔습니다.");
Random r = new Random();
for (int i=0 ; i <r.nextInt(3) + 1 ; i++)
System.out.println(name + "님이 자동차를 운전합니다.");
System.out.println(name + "님이" + where + " 에 도착했습니다.");
}
}
public class SynchroDemo {
public static void main(String[] args) {
SharedCar car = new SharedCar();
new CarThread("뺀질이", car, "서울").start();
new CarThread("멍청이", car, "부산").start();
new CarThread("멍멍이", car, "광주").start();
}
}
Object 클래스가 제공하는 락과 관련된 메소드
- 모든 객체가 Object 클래스의 후손이므로 어떤 객체든 이 메소드들을 사용할 수 있다.
- 이 메소드들은 동기화된 코드 내부에서만 의미가 있다
- 동기화된 코드가 아닌데 이 메소드를 사용하면 IllegalMonitorStateException 예외가 발생
- 메소드 종류
- wait() : 중지상태가 된다
- notify() : 하나의 스레드를 실행 대기 상태로 만든다
- notifyAll() : 공유 자원을 기다리는 모든 스레드를 실행 대기 상태로 만든다
예시
더보기
public class TotalThread extends Thread {
int total;
public void run() {
synchronized(this) {
for(int i=1 ; i<=100 ; i++)
total += i;
notify();
}
}
}
public class WaitNotifyDemo {
public static void main(String[] args) {
TotalThread t = new TotalThread();
t.start();
synchronized (t) {
try {
System.out.println("스레드 t가 끝날 때까지 대기...");
t.wait();
} catch(InterruptedException e) {}
}
System.out.println("총합 : " + t.total);
}
}
생산자와 소비자 예시
더보기
//공유 자원
public class Dish {
private boolean empty = true;
public boolean isEmpty() {
return empty;
}
public void setEmpty(boolean empty) {
this.empty = empty;
}
}
//생산자
public class Cook implements Runnable {
private final Dish dish;
public Cook(Dish dish) {
this.dish = dish;
}
private void cook(int i) throws InterruptedException {
synchronized (dish) {
while (!dish.isEmpty())
dish.wait();
dish.setEmpty(false);
System.out.println(i + "번째 음식이 준비되었습니다.");
dish.notify();
}
}
@Override
public void run() {
for(int i=0 ; i<5 ;i++) {
try {
cook(i);
Thread.sleep(50);
} catch (InterruptedException ex) {}
}
}
}
// 소비자
public class Customer implements Runnable {
private final Dish dish;
public Customer (Dish dish) {
this.dish = dish;
}
private void eat(int i) throws InterruptedException {
synchronized (dish) {
while(dish.isEmpty())
dish.wait();
dish.setEmpty(true);
System.out.println(i + "번쨰 음식을 먹었습니다.");
dish.notify();
}
}
@Override
public void run() {
for(int i=0 ; i<5 ; i++) {
try {
eat(i);
Thread.sleep(50);
}catch (InterruptedException ex) {}
}
}
}
// 메인
public class DishDemo {
public static void main(String[] args) {
final Dish d = new Dish();
new Thread(new Customer(d)).start();
new Thread(new Cook(d)).start();
}
}