티스토리 뷰

개인공부/JAVA

Thread - Sync

날따라해봐요요롷게 2021. 9. 10. 16:24
class Bank{
	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 minus) {
		int m = this.getMoney();
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		setMoney(m - minus);
	}

	public int getMoney() {
		return money;
	}
	public void setMoney(int money) {
		this.money = money;
	}
	
}

class Park extends Thread {

	@Override
	public void run() {
		System.out.println("saveThread 실행!");
		SyncMain.myBank.saveMoney(3000);
		System.out.println("남은 돈 : " + SyncMain.myBank.getMoney());
	}
}

class Kim extends Thread {

	@Override
	public void run() {
		System.out.println("minusThread 실행!");
		SyncMain.myBank.minusMoney(1000);
		System.out.println("남은 돈 : " + SyncMain.myBank.getMoney());
	}
}

public class SyncMain {
	public static Bank myBank = new Bank();	// 객체 자체를 static 으로 생성
	public static void main(String[] args) {
		Park park = new Park();
		park.start();
		
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		Kim kim = new Kim();
		kim.start();
	}
}

=======================================출력==========================================
saveThread 실행!
minusThread 실행!
남은 돈 : 9000
남은 돈 : 13000

위의 코드를 살펴보면 Bank 클래스를 생성한 후 해당 클래스의 메소드로 save 와 minus를 생성한다.

각각의 메소드에서 스레드 sleep을 설정하여 스레드가 실행되는데 시간의 갭을 둔다.

 

SyncMain 함수에서 Bank 클래스의 객체를 생성한다. 해당 객체는 static으로 전역 객체로 생성한다.

그 이유는 Park과 Kim 의 클래스에서 해당 객체의 메소드를 사용하기 위해서이다.

 

Park과 Kim 클래스는 Thread 클래스를 extends 하였다.

상속받은 클래스는 run() 메소드를 오버라이딩 한다. 

스레드 실행 시 만들어놓은 static 객체인 myBank 를 사용한다. ==>

Park ==> SyncMain.myBank.saveMoney(값);

Kim ==> SyncMain.myBank.minusMoney(값);

(static 으로 만들어놓은 변수, 객체는 클래스명.객체명.메소드명 으로 사용하면된다.)

 

결과 보기!!

출력된 내용을 보면 park의 스레드가 먼저 실행되어 saveMoney -> minusMoney 메소드가 차례로 실행된다.

하지만, 결과는 남은 돈 = 9000 -> 남은 돈 : 13000이 출력된다.

만약 kim의 결과가 9000 이라면 후에 3000원이 save되어 12000원이 되어야 하는데 13000이 된걸 볼 수 있다.

 

이러한 결과가 나타나는 이유를 알아보자

위는 스레드가 실행되는 상황을 보여준다.

두 스레드는 하나의 resource은 m을 공유하고 있다.

park의 스레드가 실행 된 후 3초의 sleep이 일어나는 중간에 kim의 스레드가 실행이 되어 각각 하나의 resource인 m을 동시에 사용하는 경우가 발생하게 된다.

park이 실행되는 도중 kim이 실행되어 minus를 일으키고 그 값인 9000이 출력 된다.

하지만 park은 sleep인 상태로 변경된 m = 9000의 값을 getMoney() 하지 못하고 맨 처음 실행하면서 갖고 있던 resource인 m = 10000을 갖고 save 3000 을 하게 된다. 

따라서 결과는 남은 돈 = 9000 -> 남은 돈 : 13000이 된다.

 

이를 통해서 하나의 resource를 사용하는 경우 우리는 동기화를 통해서 하나의 스레드가 resource를 사용하는 경우 lock을 걸어 다른 스레드가 사용하지 못하게 해야한다.

 

그렇다면 동기화는 어떤 작업을 하는가? 

 

  • 두 개의 thread 가 같은 객체에 접근 할 경우, 동시에 접근 함으로써 오류가 발생
  • 동기화는 임계영역에 접근한 경우 공유자원을 lock 하여 다른 thread의 접근을 제어
  • 동기화를 잘못 구현하면 deadlock에 빠질 수 있다.

 

## 동기화(synchronized) 를 통해 올바른 코드를 작성해보자

 

saveMoney, minusMoney 메소드를 synchronized 하여 실행을 한다.

	public synchronized 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 minus) {
		int m = this.getMoney();
		
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		setMoney(m - minus);
	}
=======================================출력============================
saveThread 실행!
minusThread 실행!
남은 돈 : 13000
남은 돈 : 12000

 

메소드에 synchronized를 설정하여 동기화 한 코드이다.

출력을 보면 두 스레드가 실행된 후 saveMoney 메소드가 실행 된 후의 결과인 13000이 출력되고 후에 12000의 결과가 출력된다.

 

# sync block 사용하기

동기화를 적용하기 위해서 위에서는 메소드에 synchronized를 적어 사용하였다.

하지만 sync block을 사용하여서 적용할 수 도 있다.

 

class Bank{
	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 minus) {
		int m = this.getMoney();
		
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		setMoney(m - minus);
	}

	public int getMoney() {
		return money;
	}

	public void setMoney(int money) {
		this.money = money;
	}
	
}

class Park extends Thread {

	@Override
	public void run() {
		synchronized (SyncMain.myBank) { 
        	// sync block 을 사용하는 경우 괄호에 lock을 걸어야 하는 shared resource를 넣어주면 된다.
			//어떤 resource 에다 sync를 걸건지 명확하게 적어주면 된다.
			System.out.println("saveThread 실행!");
			SyncMain.myBank.saveMoney(3000);
			System.out.println("남은 돈 : " + SyncMain.myBank.getMoney());
		}
	}
}

class Kim extends Thread {

	@Override
	public void run() {
		synchronized (SyncMain.myBank) {
			System.out.println("minusThread 실행!");
			SyncMain.myBank.minusMoney(1000);
			System.out.println("남은 돈 : " + SyncMain.myBank.getMoney());
		}
	}
}

public class SyncMain {
	public static Bank myBank = new Bank();	// 객체 자체를 static 으로 생성
	public static void main(String[] args) {
		Park park = new Park();
		park.start();
		
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		Kim kim = new Kim();
		kim.start();
	}
}
====================================출력=================================
saveThread 실행!
남은 돈 : 13000
minusThread 실행!
남은 돈 : 12000

출력에서 찍혀진 로그를 살펴보면 위에 다르다는 점을 볼 수 있다.

saveThread가 실행된 후 결과를 내고, 값을 로그로 찍은 후 minucThread가 실행되는 것을 볼 수 있다.

출력이 다르게 나타난 이유는 로그를 찍는 것 또한 sync block 안에 있기 때문에 동기화로 인하여 해당 스레드가 실행되는 동안 lock을 하여 다른 스레드가 실행되지 않고 실행 중인 스레드의 수행이 완료된 후 다음 스레드가 실행되게 된다.

'개인공부 > JAVA' 카테고리의 다른 글

Exception - Runnable(학교공부)  (0) 2021.11.02
Exception - throws (학교공부)  (0) 2021.09.17
Exception  (0) 2021.09.09
배열, 기본클래스  (0) 2021.05.05
JAVA - Inner Class, Lambda , Stream  (0) 2021.03.21
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함