본문 바로가기

언어공부/Java

자바 쓰레드 동기화(synchronization)

자바 쓰레드 동기화(synchronization)에 대해서 알아보자.

동기화(synchronization)

싱글쓰레드 프로세스의 경우 프로세스 내에서 단 하나의 쓰레드만 작업하기 때문에 프로세스의 자원을 가지고 작업하는데 별 문제가 없지만, 멀티쓰레드 프로세스의 경우 여러 쓰레드가 같은 프로세스 내에 자원을 공유해서 작업하기 때문에 서로의 작업에 영향을 주게 된다. 

 

만일 쓰레드A가 작업하던 도중에 다른 쓰레드B에게 제어권이 넘어갔을 때, 쓰레드A가 작업하던 공유데이터를 쓰레드B가 임의로 변경하였다면, 다시 쓰레드A가 제어권을 받아서 나머지 작업을 마쳤을 때 의도했던 것과는 다른 결과를 얻을 수 있다.

 

이러한 일을 방지하기 위해서 쓰레드의 동기화가 필요하며, 한 쓰레드가 진행 중인 작업을 다른 쓰레드가 간섭하지 못하도록 막는것을 뜻한다. 공유 데이터를 사용하는 코드 영역을 임계 영역으로 지정해놓고, 공유 데이터(객체)가 가지고 있는 lock을 획득한 단 하나의 쓰레드만 이 영역 내의 코드를 수행할 수 있게 한다.

 

synchronized를 이용한 동기화는 두가지 방법이 있다.

 

1. 메소드 전체를 임계영역으로 지정.

 public synchronized void withdraw(int money){

}

 

synchronized메소드가 호출된 시점부터 해당 메소드가 포함된 객체의 lock을 얻어 작업을 수행하다가 메소드가 종료되면 lock을 반납한다.

 

2. 특정한 영역을 임계 영역으로 지정.

synchronized(객체의 참조변수){

}

 

참조변수는 락(lock)을 걸고자하는 객체를 참조하는 것이어야 한다. synchronized블럭의 영역 안으로 들어가면서 부터 쓰레드는 지정된 객체의 lock을 얻게 되고, 이 블럭을 벗어나면 lock을 반납한다.

 

두 방법 모두 lock의 획득과 반납이 모두 자동적으로 이루어지므로 우리가 해야 할 일은 그저 임계 영역만 설정해주는 것 뿐이다. 모든 객체는 lock을 하나씩 가지고 있으며, 해당 객체의 lock을 가지고 있는 쓰레드만 임계영역의 코드를 수행할 수 있다. 그리고 다른 쓰레드 들은 lock을 얻을 때 까지 기다리게 된다. 동기화를 최소화해야 좋다.

 

ex) 동기화를 하지 않았을 때

 

출금 메소드를 동기화 하지 않으면 A쓰레드가 작업하던 도중 B쓰레드로 넘어가서 공유데이터를 임의로 변경하게 되어 잔고가 음수가 될 수 있다. 한 쓰레드가 if문의 조건식을 통과하고 출금하기 바로 직전에 다른 쓰레드가 끼어들어 출금을 먼저하기 때문이다.

 

출력화면

 

 

 

ex)동기화를 했을 때

 

이전 예제에서 출금 메소드에 synchronized를 붙였다. 동기화를 했기 때문에 음수값이 나오지 않는다. 여기서 money는 private여야 외부에서 직접 접근할 수 없다.

 

 

ex)동기화 시킨 다른 코드

 

 

출력화면