본문 바로가기
Kosta DevOps 과정 280기/Java

임계영역 (Critical Section)

by 롯슈83 2024. 5. 30.

정의

  • 두 개 이상의 쓰레드가 자원을 공유할 때 한 번에 하나의 쓰레드에게만 접근을 허용하는 영역을 말한다.
  • 자바에서는 그러한 동작을 하는 메소드 이름 앞에 혹은 그러한 기능을 하는 블럭 {} 앞에 synchronized 키워드를 붙이면, 자동으로 임계영역이 되며 한번에 하나의 쓰레드에게만 접근을 허용하게 된다.
    • 쓰레드를 가동시키면 가능하면 동시다발로 공평하게 실행이 되게끔 스케줄링을 해준다. 만약 두개의 객체가 반듸 1대 1로 동작하도록 하려면 쓰레드 사이의 통신을 이용할 수있다.

임계 영역을 설정하지 않고 공유 자원에 접근하였을때 발생되는 문제와 구현 코드

package com.kosta.exam05;

//모금액을 위한 클래스 : 목표 = 500000;
public class Account {
	private int balance = 0;
	
	@Override
	public String toString() {
		return "Account [balance=" + balance + "]";
	}

	public int getBalance() {
		return balance;
	}
	
	public void call(int amount) {
		balance += amount;
	}
}
package com.kosta.exam05;

//성금자를 위한 클래스(전화를 걸어서 모금액을 증가시키도록 한다.)
//다른 성금자와 관계없이 계속하여 입금을 위하여 쓰레드를 계속 상속받는다.
public class Person extends Thread{
	//성금자 이름을 위한 멤버변수
	String name;
	
	//모금액 객체를 매개변수로 전달바당 초기화해준다.
	public Person(String name, Account account) {
		super();
		this.name = name;
		this.account = account;
	}

	//다른 성금자와 모금액을 공유하기 위하여 모금액 클래스인  Account 를 멤버 변수로 선언한다.
	Account account;

	//성금자가 해야할 일을 run을 오버라이딩하여 써준다.
	@Override
	public void run() {
		//1000월씩 10번 입금하도록 하기
		for(int i = 1; i <=10; i++) {
			account.call(1000);
			System.out.println(name+"이"+i+"번 입금하였습니다.(통장 : "+account.getBalance()+"원에서 1000원 입금)");
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}	
}
package com.kosta.exam04;

public class TVARSTest {

	public static void main(String[] args) {
		//모금액을 위한 Account 객체를 생성
		Account account = new Account();
		
		//성금자의 이름과 모금액을 위한 account 객체를 매개변수로 하여 5명의 성금자 객체를 생성한다.
		Person p1 = new Person("A", account);
		Person p2 = new Person("B", account);
		Person p3 = new Person("C", account);
		Person p4 = new Person("D", account);
		Person p5 = new Person("E", account);
		
		//5명의 성금자가 경쟁적으로 입금을 하기 위하여 쓰레드를 가동시킨다.
		p1.start();
		p2.start();
		p3.start();
		p4.start();
		p5.start();
		
		//쓰레드가 다 실행되기도 전에 출력이 찍힌다.
		System.out.println("전체모금액 : "+account.getBalance());
	}
}
/*
전체모금액 : 2000
C이1번 입금하였습니다.(통장 : 3000원에서 1000원 입금)
E이1번 입금하였습니다.(통장 : 5000원에서 1000원 입금)
A이1번 입금하였습니다.(통장 : 2000원에서 1000원 입금)
B이1번 입금하였습니다.(통장 : 1000원에서 1000원 입금)
...
--> 알아서 스케쥴링 해주기 때문에 오래 걸릴거같아서 입금은 미뤄지고 총액부터 찍히는 것이다. 
 * */
package com.kosta.exam05;

public class TVARSTest {

	public static void main(String[] args) {
		//모금액을 위한 Account 객체를 생성
		Account account = new Account();
		
		//성금자의 이름과 모금액을 위한 account 객체를 매개변수로 하여 5명의 성금자 객체를 생성한다.
		Person p1 = new Person("A", account);
		Person p2 = new Person("B", account);
		Person p3 = new Person("C", account);
		Person p4 = new Person("D", account);
		Person p5 = new Person("E", account);
		
		//5명의 성금자가 경쟁적으로 입금을 하기 위하여 쓰레드를 가동시킨다.
		p1.start();
		p2.start();
		p3.start();
		p4.start();
		p5.start();
		
		try {
			p1.join();
			p2.join();
			p3.join();
			p4.join();
			p5.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//쓰레드가 다 실행되기도 전에 출력이 찍힌다.
		System.out.println("전체모금액 : "+account.getBalance());
	}
}
/*
...
C이10번 입금하였습니다.(통장 : 43000원에서 1000원 입금)
B이10번 입금하였습니다.(통장 : 44000원에서 1000원 입금)
D이10번 입금하였습니다.(통장 : 45000원에서 1000원 입금)
E이10번 입금하였습니다.(통장 : 45000원에서 1000원 입금)
A이10번 입금하였습니다.(통장 : 46000원에서 1000원 입금)
전체모금액 : 46000

-> join() 현재 스레드가 죽을 때까지 기다리라는 메소드.
-> 스레드가 다 죽고 옴
-> 아직 임계영역을 설정하지 않았기 때문에 전체 모금액이 올바르지 않다.
 * */
  • 공유자원에 두 개 이상의 쓰레드가 한번에 접근해서 이런 현상이 있을 수 있다. 한번에 하나의 쓰레드에게만 접근을 허용하려면 "임계영역"을 설정해야한다. 자바에서는 임계영역 설정을 위하여 메소드 이름 앞에 synchronized 키워드를 붙인다.
package com.kosta.exam07;

//모금액을 위한 클래스 : 목표 = 500000;
public class Account {
	private int balance = 0;
	
	@Override
	public String toString() {
		return "Account [balance=" + balance + "]";
	}

	public int getBalance() {
		return balance;
	}
	
	//임계영역 설정을 위하여 synchronized 키워드를 붙여줍니다.
	public synchronized void call(String name, int n, int amount) {
		System.out.println(name + "의 "+n+"번째 입금");
		if(balance >= 500000) {
			System.out.println("입금실패!");
			return;
		}
		balance += amount;
	}
}
package com.kosta.exam07;

//성금자를 위한 클래스(전화를 걸어서 모금액을 증가시키도록 한다.)
//다른 성금자와 관계없이 계속하여 입금을 위하여 쓰레드를 계속 상속받는다.
public class Person extends Thread{
	//성금자 이름을 위한 멤버변수
	String name;
	
	//모금액 객체를 매개변수로 전달바당 초기화해준다.
	public Person(String name, Account account) {
		super();
		this.name = name;
		this.account = account;
	}

	//다른 성금자와 모금액을 공유하기 위하여 모금액 클래스인  Account 를 멤버 변수로 선언한다.
	Account account;

	//성금자가 해야할 일을 run을 오버라이딩하여 써준다.
	@Override
	public void run() {
		//1000월씩 계속 입금하도록 하기
		for(int i = 1; ; i++) {
			//모금액이 500000원 이상이면 탈출
			if(account.getBalance() >= 500000) {
				break;
			}
			account.call(name, i, 1000);
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	
}

  • synchronize : 순차적으로 실행하게 한다. 따라서 if 문이 해당 명령어 밖에 있으면, 동시에 실행됐을 때 이미 그 명령어를 실행하려고 한다. 그 때에 synchronize를 만나면서 그 때서야 순차적으로 실행하려고 하는것이다. 따라서 if 또한 synchronize안에 있어야 한다.

'Kosta DevOps 과정 280기 > Java' 카테고리의 다른 글

GUI와 CUI  (0) 2024.05.30
쓰레드 통신  (0) 2024.05.30
멀티스레드-2  (0) 2024.05.30
예외 처리  (0) 2024.05.28
문자열 처리  (0) 2024.05.28