본문 바로가기

코딩/JAVA

JAVA 28-1. 쓰레드2

쓰레드 동기화 Synchronized

휴대폰으로 음악이나 영상을 동기화하면 동기화가 끝날때까지 다른 작업을 할 수 없는데 이런 구조가 바로 동기화 쓰레드의 구조이다. 두 개 이상의 쓰레드가 하나의 자원을 공유할 경우 동기화 문제가 발생한다. 변수는 하나인데 두 개의 쓰레드가 하나의 동시에 한 변수의 값을 변경하려고 하기 때문이다. 이를 막기 위해 작업이 끝날때까지 다른 접촉이 발생하지 않도록 잠가둘 필요가 있다.

 

package java12;

class SyncEx implements Runnable {
	private long money = 10000;
	@Override
	public void run() {
		synchronized (SyncEx.class) { //인자가 this.SyncEx
// synchrosized 키워드가 명시되어있는 구역은
// 다른 쓰레드에서 접근하지 못하게 막는다.
			for (int i=0; i<10; i++) {
				try { 
					Thread.sleep(500);
				} catch (Exception e) {
					e.printStackTrace();
				}
				if (getMoney() <= 0) {
					break;
				}
				outMoney(1000);
			}
		} // 여기까지 타스레드 접근불가
	}
	public long getMoney() {			//getter()
		return money;
	}
/*	public void setMoney(long money) {	//setter()
		this.money = money;
	} */
	public void outMoney (long howMuch) {
		String threadName = Thread.currentThread().getName();
			if (getMoney() > 0) {
				money -= howMuch;
				System.out.println(threadName+ " - 잔액 : " +getMoney()+ "원");
			} 
			if (getMoney() == 0 ) {
				System.out.println(threadName + " - 잔액이 없습니다.");
			}
	}
}
public class Test05 {
	public static void main(String[] args) {
		SyncEx atm = new SyncEx();
		Thread mom = new Thread(atm, "엄마");
		Thread son = new Thread(atm, "아들");
		mom.start();		//start를 실행하면 내부적으로 run이 실행되므로 오버라이드 에러가 없음
		son.start();
		
	}
}

mom.start와 son.start 두개가 실행되었지만 먼저실행된 mom부분이 synchrosize로 접근을 막아 son.start는 출력이 되지않았다.

10번행을 다음과같이 바꿀경우

mom에서 6천원만 빠져나간후에 syncronized된 쓰레드가 종료된 후 son이 실행되므로 다음과 같이 결과가 나온다.

쓰레드에서 wait(), notify()

현재 실행중인 스레드가 진행중에 wait() 메서드를 만나면 일시적으로 정지되며 대기상태로 보내지고 제어권을 다른 쓰레드에게 넘긴다. 그리고 대기상태가 된 쓰레드는 notify()를 만나면 재구동 된다.

이런기법을 이용하면 두 개 이상의 쓰레드가 구동중일때 한 개의 동기화 쓰레드가 작업을 진행하면 이 작업을 완전히 마칠 때 까지 기다렸다가 다른 쓰레드의 작업이 수행되는 것이 아니라, 하나의 쓰레드 동기화가 진행중일 때에도 일시적으로 해당 쓰레드를 정지시키고 다른 쓰레드가 작업을 할 수 있게 만들 수 있다.

package java12;

import java.util.Random;
import java.util.Scanner;

class Account {
	int balance = 1000; //잔액
	public synchronized void withdraw(int money) {
		if (balance < money) {
			try {
				System.out.println("얼마 입금하실건가요?");
				wait();					// 쓰레드 정지, 대기상태
			} catch (Exception e) {}
		}
		balance -= money;
	}
	public synchronized void deposit(int money) {
		balance += money;
		notify(); 					// 대기상태인 쓰레드 실행
	}
}
class AccountThread implements Runnable {
	Account acc;			// Account 객체 acc준비
	public AccountThread(Account acc) {		//생성자
		this.acc = acc;
	}
	@Override
	public void run() {
		while (true) {
			try {
				Thread.sleep(500);
			} catch(Exception e) {}
			int money = (new Random().nextInt(3) + 1) * 100;
			acc.withdraw(money);
			System.out.println("잔액 : " +acc.balance);
		}
	}
}

public class Test06 {
	public static void main(String[] args) {
		Account acc = new Account();
		Runnable rr = new AccountThread(acc);
		Thread td = new Thread(rr);
		td.start();
		while (true) {
			Scanner scan = new Scanner(System.in);
			int n = scan.nextInt();
			acc.deposit(n);
		}
	}
}

 

 

'코딩 > JAVA' 카테고리의 다른 글

JAVA 29-1. 컬렉션 프레임워크 - MAP  (0) 2022.08.08
JAVA 29. 컬렉션 프레임워크  (0) 2022.08.08
JAVA 28. 쓰레드  (0) 2022.08.08
JAVA 27. 예외  (0) 2022.08.08
JAVA 26. 내부 클래스  (0) 2022.07.31