티스토리 뷰

주의 사항!

  • 이 글은 제가 직접 공부하는 중에 작성되고 있습니다.
  • 따라서 제가 이해하는 그대로의 내용이 포함됩니다.
  • 따라서 이 글은 사실과는 다른 내용이 포함될 수 있습니다.


공유 객체를 사용할 때의 주의할 점

싱글 스레드 프로그램에서는 한 개의 스레드가 객체를 독차지해서 사용하면 되지만, 멀티 스레드 프로그램에서는 스레드들이 객체를 공유해서 작업해야 하는 경우가 있습니다. 이 경우, 스레드 A를 사용하던 객체가 스레드 B에 의해 상태가 변경될 수 있기 때문에 스레드 A가 의도했던 것과는 다른 결과를 산출할 수도 있습니다.

 

이는 마치 여러 사람이 계산기를 함께 나눠 쓰는 상황과 같아서 사람 A가 계산기로 작업을 하다가 계산 결과를 메모리에 저장한 뒤 잠시 자리를 비웠을 때 사람 B가 계산기를 만져서 앞사람이 메모리에 저장한 값을 다른 값으로 변경하는 것과 같습니다. 그런 다음 사람 A가 돌아와 계산기에 저장된 값을 이용해서 작업을 진행한다면 결국 사람 A는 엉터리 값을 이용하게 됩니다.

 

다음 예제는 위의 예를 보여주는 예제입니다.

//Calculator.java
package Example;

public class Calculator {
	private int memory;
	
	public int getMemory() {
		return memory;
	}
	public void setMemory(int memory) {
		this.memory = memory;
		
		try {
			Thread.sleep(2000);
		} catch(Exception e) {}
		
		System.out.println(Thread.currentThread().getName() + " : " + this.memory);
	}
}
//User1.java
package Example;

public class User1 extends Thread{
	private Calculator calculator;
	
	public void setCalculator(Calculator calculator) {
		this.setName("User1");
		this.calculator = calculator;
	}
	@Override
	public void run() {
		calculator.setMemory(100);
	}
}
//User2.java
package Example;

public class User2 extends Thread{
	private Calculator calculator;
	
	public void setCalculator(Calculator calculator) {
		this.setName("User2");
		this.calculator = calculator;
	}
	@Override
	public void run() {
		calculator.setMemory(200);
	}
}
//Main.java
package Example;

public class Main {
	public static void main(String[] args) {		
		Calculator calculator = new Calculator();
		
		User1 user1 = new User1();	
		user1.setCalculator(calculator);
		user1.start();
		
		User2 user2 = new User2();
		user2.setCalculator(calculator);
		user2.start();
	}
}

/*
실행결과

User1 : 200
User2 : 200

*/

 

동기화 메서드 및 동기화 블록

스레드가 사용 중인 객체를 다른 스레드가 변경할 수 없도록 하려면 스레드 작업이 끝날 때까지 객체에 잠금을 걸어서 다른 스레드가 사용할 수 없도록 해야 합니다.

 

멀티 스레드 프로그램에서 단 하나의 스레드만 실행할 수 있는 코드 영역을 임계 영역(critical section)이라고 합니다. 자바는 임계 영역을 지정하기 위해 동기화 메서드(synchronized)와 동기화 블록을 제공합니다. 스레드가 객체 내부의 동기화 메서드 또는 블록에 들어가면 즉시 객체에 잠금을 걸어 다른 스레드가 임계 영역 코드를 실행하지 못하도록 합니다.

동기화 메서드를 만드는 방법은 다음과 같이 메서드 선언에 synchronized 키워드를 붙이면 됩니다. synchronized 키워드는 인스턴스와 정적 메서드 어디든 붙일 수 있습니다.

public synchronized void method() {
	//임계 영역 : 단 하나의 스레드만 실행할 수 있는 영역
}

 

동기화 메서드는 메서드 전체 내용이 임계 영역이므로 스레드가 동기화 메서드를 실행하는 즉시 객체에는 잠금이 일어나고, 스레드가 동기화 메서드를 실행 종료하면 잠금이 풀립니다. 메서드 전체 내용이 아니라, 일부 내용만 임계 영역으로 만들고 싶다면 다음과 같이 동기화(synchronized) 블록을 만들면 됩니다.

public void method() {
	//여러 스레드가 실행 가능한 영역

	synchronized(공유 객체) {
		//임계 영역
	}

	//여러 스레드가 실행 가능한 영역
}

동기화 블록의 외부 코드들은 여러 스레드가 동시에 실행할 수 있지만, 동기화 블록의 내부 코드는 임계 영역이므로 한 번에 한 스레드만 실행할 수 있고 다른 스레드는 실행할 수 없습니다.

 

만약 동기화 메서드와 동기화 블록이 여러 개 있을 경우, 스레드가 이들 중 하나를 실행할 때 다른 스레드는 해당 메서드는 물론이고 다른 동기화 메서드 및 블록도 실행할 수 없습니다. 하지만 일반 메서드는 실행이 가능합니다.

 

다음 예제는 이전 예제에서 문제가 된 공유 객체인 Calculator를 수정한 것입니다. Calculator의 setMemory() 메서드를 동기화 메서드로 만들어서 User1 스레드가 setMemory()를 실행할 동안 User2 스레드가 setMemoey() 메서드를 실행할 수 없도록 했습니다.

//Calculator.java
package Example;

public class Calculator {
	private int memory;
	
	public int getMemory() {
		return memory;
	}
	public synchronized void setMemory(int memory) {    //수정된 부분, 동기화 메서드로 수정
		this.memory = memory;
			
		try {
			Thread.sleep(2000);
		} catch(Exception e) {}
			
		System.out.println(Thread.currentThread().getName() + " : " + this.memory);	
	}
}
//Main.java
package Example;

public class Main {
	public static void main(String[] args) {		
		Calculator calculator = new Calculator();
		
		User1 user1 = new User1();	
		user1.setCalculator(calculator);
		user1.start();
		
		User2 user2 = new User2();
		user2.setCalculator(calculator);
		user2.start();
	}
}

/*
실행결과

User1 : 100
User2 : 200

*/

 

다음과 같이 동기화 블록을 사용할 수도 있습니다.

//Calculator.java
package Example;

public class Calculator {
	private int memory;
	
	public int getMemory() {
		return memory;
	}
	public void setMemory(int memory) {
		synchronized (this) {              //동기화 블록, 잠금 대상인 공유 객체를 매개 값으로 대입
			this.memory = memory;
			
			try {
				Thread.sleep(2000);
			} catch(Exception e) {}
				
			System.out.println(Thread.currentThread().getName() + " : " + this.memory);	
		}
	}
}

 

 

'공부 일지 > JAVA 공부 일지' 카테고리의 다른 글

자바, 스레드 상태 제어  (0) 2021.04.26
자바, 스레드 상태  (0) 2021.04.26
자바, 스레드 우선 순위  (0) 2021.04.26
자바, 작업 스레드 생성과 실행  (0) 2021.04.26
자바, 멀티 스레드 개념  (0) 2021.04.22
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/12   »
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 31
글 보관함