[java] Thread의 synchronized

이민준·2021년 9월 19일
0

java

목록 보기
4/5

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

싱글 스레드 프로그램에서는 한 개의 스레드가 객체를 독차지해서 사용하면 되지만, 멀티 스레드 프로그램에서는 스레드들이 객체를 공유해서 작업해야 하는 경우가 있습니다.

이 경우, 스레드 A를 사용하던 객체가 스레드 B에 의해서 상태가 변경될 수 있습니다.

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

}
public class Calculator{
 private int memory;
 
 public int getMemory(){
   return memory;
 }
 
 public void setMemory(int memory){
   this.memory = memory;
   try{
     Thread.sleep(2000);
   }catch(InterruptedException e){}
   System.out.println(Thread.currentThread().getName() + ": " + this.memory);
   }
 }
public class User1 extends Thread{
  private Calculator calculator;
  
  public void setCalculator(Calculator calculator){
  this.setName("User1")
  this.caculator = calculator;
  }
  
  public void run(){
    calculator.setMemory(100);
  }
}

public class User2 extends Thread{
  private Calculator calculator;
  
  public void setCalculator(Calculator calculator){
  this.setName("User2")
  this.caculator = calculator;
  }
  
  public void run(){
    calculator.setMemory(50);
  }
}

위의 코드를 실행해보면 User1의 메모리 값을 User2가 저장한 50으로 출력이 됩니다.

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

멀티 스레드 프로그램에서 단 하나의 스레드만 실행할 수 있는 코드 영역을 임계 영역이라고 합니다.

자바는 임계영역을 지정하기 위해 동기화 메소드와 동기화 블록을 제공합니다.
스레드가 객체 내부의 동기화 메소드 또는 블록에 들어가면 즉시 객체에 잠금을 걸어 다른 스레드가 임계 영역 코드를 실행하지 못하도록 합니다.

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

public synchronized void method(){
  임계 영역;
}

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

public void method(){
  synchronized(공유 객체){
    임계영역
  }
  ...

}

동기화 블록의 외부 코드들은 여러 스레드가 동시에 실행할 수 있지만, 동기화 블록의 내부 코드는 임계 영역이므로 한 번에 한 스레드만 실행할 수 있고 다른 스레드는 실행할 수 없습니다. 만약 동기화 메소드와 동기화 블록이 여러 개 있을 경우, 스레드가 이들 중 하나를 실행할 때 다른 스레드는 해당 메소드는 물론이고 다른 동기화 메소드 및 블록도 실행할 수 없습니다. 하지만 일반 메소드는 실행이 가능합니다.

위의 예시로 들었던 코드를 아래와 같이 수정할 수 있습니다.

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(InterruptedException e){}
   System.out.println(Thread.currentThread().getName() + ": " + this.memory);
   }
 }
profile
러닝커브가 장점인 개발자입니다.

0개의 댓글