이전글: 

2020/12/13 - [JAVA] - 멀티스래드 프로그래밍-1

임계 영역

두개 이상의 스레드가 동시에 접근하게 되는 리소스

임계영역에 동시에 스레드가 접근하게 되면 실행 결과를 보장할수 없음

스레드간의 순서를 맞추는 동기화가 필요

 

동기화

임계 영역에 여러 스레드가 접근하는 경우 한 스레드가 수행하는 동안 공유자원을 lock 하여 다른 스레드의 접근을 막음

동기화를 잘못 구현하면 deadlock에 빠질수 있음

 

자바에서의 동기화

Synchronized 수행문과 Synchronized 메서드 이용 

 

먼저 Synchronized 메서드 이전에 

Synchronized 가 들어가지않는 임계 영역에 여러 스레드가 접근하는 경우를 확인해보겠습니다.

아래의 그림과 같은 형태에

클래스를 만들고자 합니다.

먼저  bank 클래스:

class Bank extends Thread{
	private int money=10000;
	public  void saveMoney(int save) {
		
			int m=this.getMoney();
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			setMoney(m+save);
		
		
	}
	
	public void minusMoney(int mins) {
		
			int m=this.getMoney();
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			setMoney(m-mins);
		
		
		
		
		
	}
	
	
	public int getMoney() {
		return money;
	}
	
	public void setMoney(int money) {
		this.money=money;
	}
}

 bank에서 saveMoney는 3초후에 돈이 들어가고 minMoney는 0.2초후에 돈이 빠집니다.

 

park, parkWife 클래스입니다 각각 쓰레드로 bank 임계영역에 존재하는 money라는 공유 자원에 역할을 수행합니다. 

park은 saveMoney ,parkWife는 mineMoney를 합니다. 


class Park extends Thread{
	public void run() {
		
		System.out.println("start save");
		Main.myBank.saveMoney(3000);
		System.out.println("save Money:"+Main.myBank.getMoney());
	}
	
}
class ParkWife extends Thread{
	public void run() {
		System.out.println("start minus");
		Main.myBank.minusMoney(1000);
		System.out.println("minus Money:"+Main.myBank.getMoney());
	}
}

이 상태로 main클래스를 확인하면 

public class Main {

	public static Bank myBank=new Bank();
	
	public static void main(String [] args) throws InterruptedException {
		Park p=new Park();
		p.start();
		Thread.sleep(200);
		ParkWife pw=new ParkWife();
		pw.start();
	}
	
	
}

맨 먼저 park이 저축을하고 ->0.2초후에 parkWife가 돈을 소비합니다. 

결과는 당연히 10000+3000-1000=12000원이 남아야합니다.

그렇다면 결과를 확인해보면 

 우리가 예상한데로 나오는게 아니라 13000원 이라는 결과가 나오게 됩니다.

이유는 위에서 언급했던 임계영역에서 공유자원을 동시에 접근해서 그렇습니다. 

즉 10000원이라는 bank 클래스에 존재하는 자원을 park이 먼저 접근했지만 아직 sleep으로 인해 아직 정산되지 않는 상태에서 parkWife가 접근하여 9000원을 먼저 계산하고 이미 이전에 park은 10000원을 가지고 있는 상태에서 3000원을 더하게 되어 13000원이라는 결과가 나오게 됩니다.

그렇다면 이를 해결하기위해서 Synchronized를 이용해야합니다.

 

블록방식과 메서드 방식을 이용합니다.

코드는 아래와 같습니다. 코드를 보시고 결과만 보여 드리도록 하겠습니다. 

package Test01;

import java.io.IOException;
//synchronized 메서드 
class Bank extends Thread{
	private int money=10000;
	public synchronized void saveMoney(int save) {//public  void saveMoney(int save) {
		int m=this.getMoney();
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		setMoney(m+save);
	}
	
	public synchronized void minusMoney(int mins) {//public void minusMoney(int mins) {
		int m=this.getMoney();
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		setMoney(m-mins);
		
		
		
	}
	
	
	
	
	
	public int getMoney() {
		return money;
	}
	
	public void setMoney(int money) {
		this.money=money;
	}
}
class Park extends Thread{
	public void run() {
		
		System.out.println("start save");
		Main.myBank.saveMoney(3000);
		System.out.println("save Money:"+Main.myBank.getMoney());
	}
	
}
class ParkWife extends Thread{
	public void run() {
		System.out.println("start minus");
		Main.myBank.minusMoney(1000);
		System.out.println("minus Money:"+Main.myBank.getMoney());
	}
}
public class Main {

	public static Bank myBank=new Bank();
	
	public static void main(String [] args) throws InterruptedException {
		Park p=new Park();
		p.start();
		Thread.sleep(200);
		ParkWife pw=new ParkWife();
		pw.start();
	}
	
	
}

 

달라진점은 bank 클래스에 

public synchronized void saveMoney(int save)

public synchronized void minusMoney(int mins)

synchronized 메서드를 추가해줍니다.

이번엔 synchronized 블록으로 사용하는 방법입니다.

두개의 케이스로 나눌수 있는데 먼저 

bank 부분에다가 synchronized 블록 해주는 방식을 사용하거나

Park,ParkWif에 부분에 synchronized 블록을만들어주는 방식입니다.

둘중에 하나로 바꿔줘야합니다. 

먼저 bank 부분에다가 synchronized 블록:

class Bank extends Thread{
	private int money=10000;
	public  void saveMoney(int save) {//public  void saveMoney(int save) {
		synchronized (this) {
			int m=this.getMoney();
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			setMoney(m+save);
		}
		
	}
	
	public void minusMoney(int mins) {//public void minusMoney(int mins) {
		synchronized (this) {
			int m=this.getMoney();
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			setMoney(m-mins);
		}
		
		
		
		
	}
	

다음은 Park,ParkWif에 부분에 synchronized 블록:

class Park extends Thread{
	public void run() {
		synchronized (Main.myBank) {
		System.out.println("start save");
		Main.myBank.saveMoney(3000);
		System.out.println("save Money:"+Main.myBank.getMoney());
		}
	}
	
}
class ParkWife extends Thread{
	public void run() {
		synchronized (Main.myBank) {
		System.out.println("start minus");
		Main.myBank.minusMoney(1000);
		System.out.println("minus Money:"+Main.myBank.getMoney());
		}
	}
}

 

'프로그래밍언어 > JAVA' 카테고리의 다른 글

Executor, ExecutorService  (1) 2024.03.16
<JAVA>멀티스래드 프로그래밍-3  (0) 2020.12.13
<JAVA>멀티스래드 프로그래밍-1  (0) 2020.12.13
<JAVA>Thread  (0) 2020.12.13
<JAVA>데코레이터 패턴  (0) 2020.12.13

+ Recent posts