synchronized

수호천사임다·2024년 10월 19일

자바

목록 보기
13/15

synchronized

  • 자바에서 동기화를 구현하는 키워드
  • 멀티스레드 환경에서 공유 자원에 여러 스레드가 동시에 접근하지 못하도록 임계 구역을 보호하는 데 사용
    • 가능한 최소한의 범위에 적용해야 한다.(동시에 여러 스레드가 실행할 수 있는 부분을 늘려서, 전체적인 처리 성능을 더 높일 수 있음)
  • 데이터 무결성을 유지하고, 스레드 간의 충돌을 방지할 수 있다.

멀티스레드 프로그램에서 여러 스레드가 공유 자원을 동시에 접근하면 경쟁상태 또는 데이터 불일치가 발생할 수 있다. 이를 방지하기 위해 스레드들이 순차적으로
자원에 접근할 수 있도록 동기화가 필요하다.
synchronized는 특정 객체의 모니터 락을 사용하여, 한 스레드가 자원을 사용하는 동안 다른 스레드가
해당 자원에 접근하지 못하도록 제어. 자원을 다 사용한 후에는 락을 해제하고, 다른 스레드가 접근할 수 있도록 한다.

임계 영역(critical section)

  • 여러 스레드가 동시에 접근하면 데이터 불일치나 예상치 못한 동작이 발생할 수있는 위험하고 또 중요한 코드 부분을 뜻한다.
  • 여러 스레드가 동시에 접근해서는 안 되는 공유 자원을 접근하거나 수정하는 부분을 의미한다.
    • ex) 공유 변수나 공유 객체를 수정
  • 임계 영역에 스레드가 들어가면 Runnable -> BLOCKED 상태

synchronized 메서드

  • 모든 객체는 내부에 자신만의 락(lock)를 가지고 있다.
  • 스레드가 synchronized 키워드가 있는 메서드에 진입하려면 반드시 해당 인스턴스의 락이 있어야한다.
  • synchronized 안에서 접근하는 변수의 메모리 가시성 문제는 자동으로 해결.
  • 락을 획득하는 순서는 보장되지 않는다.
synchronized 메서드 예시 코드
@Override
public synchronized boolean withdraw(int amount) {
log("거래 시작: " + getClass().getSimpleName());
log("[검증 시작] 출금액 : " + amount + ", 잔액: " + balance);
if(balance < amount){
  log("[검증 실패]" + "출금액 : " + amount + ", 잔액: " + balance);
  return false;
}

log("[검증 완료] 출금액 : " + amount + ", 잔액: " + balance);
sleep(1000);
balance -= amount;
log("[출금 완료] 출금액 : " + amount + ", 잔액: " + balance);

log("거래 종료");
return true;
}

synchronized 코드 블럭

  • 여러 스레드가 동시에 실행하지 못하기 때문에, 전체로 보면 성능이 떨어질 수 있다.
  • 공유 자원을 사용하지 전혀 사용하지 않는 곳은 동시에 실행해도 아무런 문제가 발생하지 않는다.
  • synchronized 메서드는 공유 자원을 사용하지 않는 부분도 한 번에 하나의 스레드만 실행을 할 수 있다.
  • 이러한 문제들을 해결하기 위해 synchronized 메서드가 아니라 synchronized 코드 블럭은 특정 코드 블럭에 최적화 해서 적용을 할 수 있는 기능이다.
  • synchronized (this)는 객체가 생성된 그 객체 자체에 대한 락을 걸고, 여러 스레드가 동시에 접근하지 못하게 보호하는 역할을 한다. synchronized 코드 블럭 예시 코드
@Override
public boolean withdraw(int amount) {
log("거래 시작: " + getClass().getSimpleName());

    synchronized (this) {
      // ==임계 영영 시작==
      log("[검증 시작] 출금액 : " + amount + ", 잔액: " + balance);
      if(balance < amount){
        log("[검증 실패]" + "출금액 : " + amount + ", 잔액: " + balance);
        return false;
      }

      log("[검증 완료] 출금액 : " + amount + ", 잔액: " + balance);
      sleep(1000);
      balance -= amount;
      log("[출금 완료] 출금액 : " + amount + ", 잔액: " + balance);
      // ==임계 영영 종료==
    }

    log("거래 종료");
    return true;
}

주의 사항

  • 과도한 동기화는 성능을 저하: 모든 스레드가 락을 얻기 위해 대기해야하므로, 병렬 처리가 줄어들 수 있다.
    • 10차선으로 가는 길이 1차선으로 바뀐다고 상상해보자..
  • 데드락(deadlock): 여러 자원이 서로를 기다리가 영원히 끝나지 않는 상황이 발생할 수 있다.

스레드가 동시에 접근할 수 있는 자원, 객체나 메서드에 대해서 일관성 있꼬 안전한 접긍늘 보장하기 위한 메커니즘이다.
동기화는 주로 멀티스레드 환경에서 발생할 수 있는 문제, ex) 데이터 손상이나 얘기치 않은 결과를 방지하기 위해 사용.
주로 공유 자원에 접근을 하는 거에서 문제가 발생.

이러한 문제들을 해결한게 synchronized이다. synchronized 메서드, 코드 블럭을 사용하여 여러 스레드가 동시에 실행하는 걸 막을 수 있었다.
synchronized 메서드을 사용하면 공유 자원을 시용하지 않는 부분도 오직 하나의 스레드만 실행 할 수 있기 때문에 되도록이면 synchronized 메서드
보다는 임계영역 시작과 종료되는 부분을 잘 구분해서 synchronized 코드 블럭을 사용하는 것이 좋을 거 같다.
이런 동기화를 사용하면 경합 조건(두 개 이상의 스레드가 경쟁적으로 동일한 자원을 수정할 때 발생하는 문제)와
데이터 일관성(여러 스레드가 동시에 읽고 쓰는 데이터의 일관성을 유지)를 해결 할 수 있었다.
과도한 동기화는 성능 저하가 발생할 수 있으므로 동기화는 필요한 곳에 꼭 적절하게 사용을 해야 한다.
그리고 synchronized을 사용하면 메모리 동시성 문제까지 해결 할 수 있으니 너무나 좋은 기능인 거 같다.

0개의 댓글