[Java] 동기화(synchronized) 메소드/블록

Hyo Kyun Lee·2022년 2월 3일
0

Java

목록 보기
43/49

1. 기존 공유객체 사용의 문제점

일전 공유객체를 사용하여, 여러개의 스레드가 공유객체의 메소드를 활용하는 상황이 일어난다고 가정해보자.

public class MusicBox{
	public void PlayMusicA(){
    	System.out.println("played music A");
    }
    
    public void PlayMusicB(){
		System.out.println("played music B");
    }
}
public class MusicBoxTester{
	public void main(String[] args){
		MusicBox box = new MusicBox();
        
  		//공유객체를 실행하는 스레드를 type(생성자)에 따라 여러개 생성
        MusicBoxListener person1 = new MusicBoxListener(1, box);
        MusicBoxListener person2 = new MusicBoxListener(2, box);
        
        //MusicBoxListener 객체에서 run 메소드 실행
        //생성자를 통해 box 객체가 전달되면서, 해당 type에 맞게 switch문 실행
        //전달된 객체 모두 동일한 공유 객체를 가지며, 해당 객체에서 type과 관련한 메소드를 실행
        person1.run();
        person2.run();
    }
}

위 MusicBoxTester의 main thread를 실행하면 playMusicA메소드와 playMusicB메소드가 순서 상관없이 뒤죽박죽 호출 및 실행된다.
이를 다시 말하면, block 내부에서는 person1을 통한 MusicA 메소드 → person2을 통한 MusicB 메소드 순서로 호출되었지만 실제 실행되는 순서는 작성된 순서를 따르지 않는다는 것과 같다.

2. Monitoring lock

이러한 순서 상관없이 뒤죽박죽 실행이 되는 것을 방지하는 방안 중 하나로, 한 메소드가 실행될 때는 메소드가 완전히 실행종료될때까지 다른 메소드의 실행을 lock(실행보류 및 대기)상태로 두는 것을 의미한다.

공유객체의 method에 synchronized 키워드를 붙인다.

public class MusicBox{
	public synchronized void PlayMusicA(){
    	System.out.println("played music A");
    }
    
    public synchronized void PlayMusicB(){
		System.out.println("played music B");
    }
}

위 공유객체의 메소드가 실행되면 메소드A가 실행이 모두 완료될때까지 B가 실행되지 않고 대기하며, A 메소드 실행이 모두 완료가 되었을때 B 메소드가 실행된다.

  • 참고로 monitoring lock이 걸린 상태에서는, 메소드가 완전히 실행종료되거나 특정 키워드 및 명령어를 만나기 전까지 풀리지 않고 실행보류 상태가 지속된다.

  • 만약 두개의 메소드에 대해 synchronized를 설정하고, 나머지 1개의 메소드에 대해서는 설정하지 않는다면 1개의 메소드는 다른 메소드의 실행여부에 상관없이 동시 실행될 수 있다(=monitoring lock 대상이 아님).

3. 메소드 일부에 대한 Monitoring lock

위와 같이 메소드 전체에 대한 monitoring lock을 설정할 경우 한 메소드의 실행길이 및 시간이 길어지면 다른 메소드들이 그만큼 비정상적으로 lock 상태를 유지해야 한다는 단점이 있다.

이럴 경우, 메소드 일부에 대한 monitoring lock을 설정하여 부분적인 synchronized 처리를 해줄 수 있는데, 이러한 과정을 synchronized block 처리리라 일컫는다.

public void playMusicA(){
	synchronized (this){
    	//이 실행부분에 한해서만 locking 처리한다.
        System.out.println("MusicA");
    }
}

위처럼 메소드 전체가 아니라, 메소드 실행 block 중 일부에 대해서만 blocking 처리를 해준다면, 말 그대로 해당 실행부분만 실행하고 바로 다음 메소드가 실행된다.

실행부분을 객체로 넣는다는 점에 유의한다(this).

0개의 댓글