동기화 ?

더보기

동기화란?

- 멀티 스레드(Multi Thread) 환경에서는 여러 스레드가 동일한 프로세스 내의 자원을 공유해서 사용하기 때문에 문제점이 발생할 수 있다.

- 따라서 공유 자원에 여러 개의 스레드가 동시에 접근하지 못하도록 설정하여야 하는데 이를 동기화라 한다.

- 즉, 한 스레드가 동기화된 객체를 사용하는 경우 오직 그 스레드 만이 그 객체에 접근할 수 있도록 한다.

 

크리티컬 섹션(Critical Section, 임계 영역)

공유 데이터나 리소스(메모리나 파일)에 대한 접근을 하나의 스레드로 제한한다. 즉, 동일한 프로세스 내의 스레드 간 동기화에 사용한다.

뮤텍스(Mutex) 

크리티컬 섹션과 동일한 기능을 가지지만 차이가 있다면 다른 프로세스에 속한 스레드를 제어할 수 있다.

세마포어(Semaphore) 

뮤텍스와 유사하지만 동시에 수행할 수 있는 스레드를 여러 개로 제한할 수 있다.

모니터(Monitors) 

두 개의 스레드에 의해 공유되고 그 값이 참조될 때 반드시 동기화되어야 하는 공유 데이터와 같은 객체를 상태변수(condition variables)라고 한다. 자바 언어에서는 상태 변수에 대한 모니터를 사용할 수 있도록 함으로써 스레드를 동기화 한다. 모니터는 자신이 소유한 스레드를 제외한 다른 스레드의 접근을 막는다.

교착 상태(DeadLock)

두 개 이상의 작업이 서로 상대방의 작업이 끝나기만을 기다리고 있기 때문에 결과적으로 아무 것도 완료되지 못하는 상태를 가리킨다.

 

Synchronized 키워드

- Synchronized 키워드를 통해 해당 작업과 관련된 공유 데이터에 lock을 걸어서 먼저 작업 중이던 스레드가 작업을 완전히 마칠 때까지는 다른 스레드에게 제어권이 넘어가더라도 데이터가 변경되지 않도록 보호함으로써 스레드의 동기화를 가능하게 한다.

Ex001, Ex003 : 동기화하지 않은 경우

더보기
package threadEx2;

public class Ex001_Synchronized {
	public static void main(String[] args) {
		MyBank1 b = new MyBank1();
				
		Thread t1 = new Thread(b);
		Thread t2 = new Thread(b);
		
		t1.start();
		t2.start();
	}
}

// 동기화 하지 않은 경우
class MyBank1 implements Runnable {
	private int money = 10000;
	
	
	@Override
	public void run() {
		int need = 6000;
		int n = 0;
		String msg = null;
		
		try {
			if(getMoney()>=need) {
				Thread.sleep(200);
				n = drawMoney(need);
				msg = "인출 성공";
			} else {
				n = 0;
				msg = "인증 실패";
			}
			
			System.out.println(msg + ", 인출금액:"+n+", 잔고:"+getMoney());
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}


	public int getMoney() {
		return money;
	}


	private int drawMoney(int m) {
		money -= m;
		return m;
	}
}
package threadEx2;

public class Ex003_Synchronized {
	public static void main(String[] args) {
		ShareData1 share = new ShareData1();
		
		UpThread1 t1 = new UpThread1(share, "up");
		DownThread1 t2 = new DownThread1(share, "down");
		
		t1.start();
		t2.start();
		
	}
}

// 동기화 하지 않은 경우
class ShareData1 {
	private int num = 100;
	
	public void up(String title) {
		System.out.print(title+":"+num);
		num++;
		System.out.println(", 증가->"+num);
	}
	public void down(String title) {
		System.out.print(title+":"+num);
		num--;
		System.out.println(", 감소->"+num);
	}
}

class UpThread1 extends Thread {
	private ShareData1 share;
	private String title;
	
	public UpThread1(ShareData1 share, String title) {
		this.share = share;
		this.title = title;
	}
	
	public void run() {
		for(int i=0; i<5; i++) {
			try {
				sleep(500);
				share.up(title);
			} catch (Exception e) {
			}
		}
	}
}

class DownThread1 extends Thread {
	private ShareData1 share;
	private String title;
	
	public DownThread1(ShareData1 share, String title) {
		this.share = share;
		this.title = title;
	}
	
	public void run() {
		for(int i=0; i<5; i++) {
			try {
				sleep(500);
				share.down(title);
			} catch (Exception e) {
			}
		}
	}
}

Ex002, Ex004 : 동기화한 경우

더보기
package threadEx2;

public class Ex002_Synchronized {
	public static void main(String[] args) {
		MyBank2 b = new MyBank2();
				
		Thread t1 = new Thread(b);
		Thread t2 = new Thread(b);
		
		t1.start();
		t2.start();
	}
}

// 동기화 한 경우
class MyBank2 implements Runnable {
	private int money = 10000;
	
	
	@Override
	public void run() {
		int need = 6000;
		int n = 0;
		String msg = null;
		
		try {
			synchronized (this) { // 동기화 블럭
				if(getMoney()>=need) {
					Thread.sleep(200);
					n = drawMoney(need);
					msg = "인출 성공";
				} else {
					n = 0;
					msg = "인증 실패";
				}				
			}
			
			System.out.println(msg + ", 인출금액:"+n+", 잔고:"+getMoney());
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}


	public int getMoney() {
		return money;
	}


	private int drawMoney(int m) {
		money -= m;
		return m;
	}
}

Ex005_wait : 대기

더보기
package threadEx2;

public class Ex005_wait {
	public static void main(String[] args) {
		MyBank3 atm = new MyBank3();
		
		Thread t1 = new Thread(atm, "t1");
		Thread t2 = new Thread(atm, "t2");
		
		t1.start();
		t2.start();
		
	}
}

class MyBank3 implements Runnable {
	private long money = 10000;
	
	public void run() {
		synchronized (this) {
			for(int i = 0; i < 10; i++) {
				if(getMoney() <= 0) {
					// wait()에 의해 대기하고 있는 모든 스레드를 깨운다.
					this.notifyAll();
					break;
				}
				
				drawMoney(1000);
				
				if(getMoney()>=2000 && getMoney() % 2000 == 0) {
					try {
						// wait()를 만나면 해당 쓰레드는 해당 객체의 모니터링 락에 대한 권한을 가지고 있으면
						// 모니터링 락의 권한을 놓고 대기한다.
						// synchronized 블록에서만 사용
						this.wait();
					} catch (Exception e) {
					}
				} else {
					// wait()에 의해 대기하고 있는 해당 스레드를 깨운다.
					// 아래 notify()를 주석 처리하면 무한 대기(deadlock)상태가 된다.
					this.notify();
				}
				
			}
			
		}
	}

	public long getMoney() {
		return money;
	}
	
	public void drawMoney(long m) {
		System.out.print(Thread.currentThread().getName()+", "); // 현재 쓰레드명 출력
		
		if(getMoney()>=m) {
			money -= m;
			System.out.printf("잔액 :  %,d원\n", getMoney());
		} else {
			System.out.println("잔액이 부족 합니다.");
		}
	}
}

 

+ Recent posts