위로 아래

임계영역과 동기화

임계영역 (critical section)

  1. 한 번에 하나씩만 처리
  2. 다수의 스레드가 공유 자원을 참조하는 코드 영역

 

스레드 동기화 (synchronization)

  1. 다수의 스레드가 공유 자원을 충돌 없이 사용할 수 있게 돕는다.
  2. 공유 자원에 배타적이고 독점적으로 접근할 수 있는 방법
  3. 임계영역을 동기화하려고 제공하는 기능
  4. 스레드가 synchronized로 지정된 동기화 블록에 진입하면 락(Lock)을 걸고, 그 블록을 벗어날 때 락을 푼다
  5. 동기화 블록에 진입한 스레드가 코드를 실행하는 동안, 다른 스레드는 락이 풀릴 때까지 모두 중지시킨다.

 

주의점

  1. 모든 스레드가 공정하게 공유자원을 사용할 수 있도록 하지 않으면 다음 문제들이 발생할 수 있다.
  2. 기아 (starvation) : 다른 작업을 수행하지 못한 채 하나 이상의 스레드가 공유 자원을 얻으려고 계속 대기하는 상태
  3. 교착 (deadlock) : 2개 이상의 스레드가 서로 어떤 작업을 수행해 주기를 기다리는 상태

 

 

 


임계영역을 동기화시키는 방법

  1. 메소드에 synchronized 지정
  2. 코드의 일부에 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 클래스가 제공하는 락과 관련된 메소드

  1. 모든 객체가 Object 클래스의 후손이므로 어떤 객체든 이 메소드들을 사용할 수 있다.
  2. 이 메소드들은 동기화된 코드 내부에서만 의미가 있다
  3. 동기화된 코드가 아닌데 이 메소드를 사용하면 IllegalMonitorStateException 예외가 발생
  4. 메소드 종류
    1. wait() : 중지상태가 된다
    2. notify() : 하나의 스레드를 실행 대기 상태로 만든다
    3. 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();
	}
}