/ JAVAJUNGSUK

Ch13-30~33. synchronized

자바의 정석 기초편

0. 목차



Chapter13. 쓰레드

Ch13 - 30. 쓰레드의 동기화(synchronization)

Ch13 - 31. synchronized를 이용한 동기화

Ch13 - 32. synchronized를 이용한 동기화 예제1

Ch13 - 33. synchronized를 이용한 동기화 예제2



Ch13 - 30. 쓰레드의 동기화(synchronization)


▶ 쓰레드의 동기화(synchronization)란?

▷ 멀티 쓰레드 프로세스에서는 다른 쓰레드의 작업에 영향을 미칠 수 있음
  • 멀티 쓰레드 프로세스에서는 여러 쓰레드가 같은 자원을 공유 메모리도 공유
  • 쓰레드A가 메모리를 사용하고 있는데 쓰레드B가 이걸 빼앗으면?
  • 멀티 쓰레드는 이렇듯 다른 쓰레드의 작업에 영향을 미칠 수 있음
▷ 진행 중인 작업이 다른 쓰레드에게 간섭받지 않게 하는 것 = 동기화
▷ 동기화하려면 간섭받지 않아야 하는 문장들을 ‘임계 영역’으로 설정
  • 임계 영역(critical section)
    ▷ 임계 영역은 lock을 얻은 단 하나의 쓰레드만 출입가능(1객체 1락)




Ch13 - 31. synchronized를 이용한 동기화


▶ synchronized란?

▷ synchronized로 임계 영역(lock 걸리는 영역)을 설정하는 데 사용하는 키워드


▶ 임계 영역의 범위와 개수

▷ 임계 영역의 범위와 개수는 최소화로 설정
▷ 임계 영역이 많을 수록 성능 저하
  • 멀티 쓰레드의 장점은 동시에 여러 기능이 가능한 것
  • 그런데 임계 영역은 한 번에 한 쓰레드만 처리 가능

▶ synchronized로 임계 영역을 설정하는 2가지 방법

▷ 메서드 전체를 임계 영역으로 지정
// ♡
public synchronized void calcSum() {     
    ...
}
// ♥

// ♡ ~ ♥ : 임계 영역
▷ 예시
// ♡
public synchronized void withdraw(int money) {
    if (balance >= money) {
        try {
            Thread.sleep(1000);
        } catch(Exception e) { }
        
        balance -= money;
    }
}
// ♥


// ♡ ~ ♥ : 임계 영역
▷ 특정한 영역을 임계 영역으로 지정
// ♡
synchronized(객체의 참조변수) {
    ...
}
// ♥

// ♡ ~ ♥ : 임계 영역
▷ 예시

public void withdraw (int money) {
    synchronized (this) { // ♡
        if (balance >= maney) {
            try {
                Thread.sleep(1000);
            } catch(Exception e) { }
        
        balance -= money;
        } 
    } // ♥
}

// ♡ ~ ♥ : 임계 영역


Ch13 - 32. synchronized를 이용한 동기화 예제1


▶ 동기화 하지 않고 출금하기

▷ 잔금이 츨금보다 클 때만 출금가능
잔금 - 출금 = +
▷ 하지만 동기화 하지 않으면 마이너스 통장도 아닌데 잔고가 -
class Ex13_12 {
	public static void main(String args[]) {
		Runnable r = new RunnableEx12();
		new Thread(r).start(); // ThreadGroup에 의해 참조되므로 gc대상 아님
		new Thread(r).start(); // ThreadGroup에 의해 참조되므로 gc대상 아님
	}
}

class Account {
private int balance = 1000; // balance : 잔고

	public  int getBalance() {
		return balance;
	}

	public void withdraw(int money){
		if(balance >= money) {
			try { Thread.sleep(1000);} catch(InterruptedException e) {}
			balance -= money;
		}
	} // withdraw
}

class RunnableEx12 implements Runnable {
Account acc = new Account();

	public void run() {
		while(acc.getBalance() > 0) {
			// 100, 200, 300중의 한 값을 임의로 선택하여 출금(withdraw)
			int money = (int)(Math.random() * 3 + 1) * 100;
			acc.withdraw(money);
			System.out.println("balance:"+acc.getBalance());
		}
	} // run()
}

// console
balance:900
balance:900
balance:700
balance:400
balance:100
balance:100
balance:100
balance:-100 // ?? 마이너스 통장도 아닌데 -가 왜 나와??
balance:-200 // ?? 마이너스 통장도 아닌데 -가 왜 나와??



Ch13 - 33. synchronized를 이용한 동기화 예제2


▶ 동기화 하고 출금하기

- 잔고 출력되지 않음
class Ex13_13 {
	public static void main(String args[]) {
		Runnable r = new RunnableEx13();
		new Thread(r).start();
		new Thread(r).start();
	}
}

class Account2 {
	private int balance = 1000; // private으로 해야 동기화가 유의미 함 : 지정 쓰레드 외엔 못 들어오게 했는데 외부에서 접근 가능하면 안됨

	public synchronized int getBalance() { // 읽어 오는 과정 부터 동기화 해야 함, 읽을 때 다른 거 읽어오면 안되니까
		return balance;
	}

	public synchronized void withdraw(int money){ // synchronized로 메서드를 동기화
		if(balance >= money) {
			try { Thread.sleep(1000);} catch(InterruptedException e) { }
			balance -= money;
		}
	} // withdraw
}

class RunnableEx13 implements Runnable {
	Account2 acc = new Account2();

	public void run() {
		while(acc.getBalance() > 0) {
			// 100, 200, 300중의 한 값을 임으로 선택해서 출금(withdraw)
			int money = (int)(Math.random() * 3 + 1) * 100;
			acc.withdraw(money);
			System.out.println("balance:"+acc.getBalance());
		}
	} // run()
}

// console
balance:700
balance:400
balance:100
balance:0
balance:0