Java Adv 05 - Synchronized Method

Synchronized로 동시성 제어하기

image image

  • 우선 코드의 결과부터 보면 t1의 작업이 모두 수행이 되고(검증, 결과) 이 후에 t2의 작업이 수행이 된다.
  • 따라서 t1,t2가 동시에 출금하지 않아서 클라이언트는 동시성 오류를 피할 수 있게 되었다.
  • synchronized는 도대체 뭘까?

모든 객체(인스턴스) 내부에 자신만의 (lock ) 가지고 있다.

  • 모니터 락(monitor lock)이라도고 부른다.
  • 객체 내부에 있고 우리가 확인하기는 어렵다.
  • 스레드가 synchronized 키워드가 있는 메서드에 진입하려면 반드시 해당 인스턴스의 락이 있어야 한다!
  • 여기서는 BankAccount(x001) 인스턴스의 synchronized withdraw() 메서드를 호출하므로 이 인스턴스의 락이 필요하다. 스레드 t1 , t2withdraw() 를 실행하기 직전이다.

Synchronized 키워드의 동작

  • t1이 먼저 실행된다고 가정한다.
  • 스레드 t1 이 먼저 synchronized 키워드가 있는 withdraw() 메서드를 호출한다.
  • synchronized 메서드를 호출하려면 먼저 해당 인스턴스의 락이 필요하다.
  • 락이 있으므로 스레드 t1BankAccount(x001) 인스턴스에 있는 락을 획득한다.
  • 스레드 t1 은 해당 인스턴스의 락을 획득했기 때문에 withdraw() 메서드에 진입할 수 있다.
  • 스레드 t2withdraw() 메서드 호출을 시도한다. synchronized 메서드를 호출하려면 먼저 해당 인스턴스의 락이 필요하다.(언젠가는 락이 오겠지..)
  • 스레드 t2BankAccount(x001) 인스턴스에 있는 락 획득을 시도한다. 하지만 락이 없다.
  • 락이 없으면 t2 스레드는 락을 획득할 때 까지 BLOCKED 상태로 대기한다. t2 스레드의 상태는 RUNNABLE BLOCKED 상태로 변하고, 락을 획득할 때 까지 무한정 대기한다. 참고로 BLOCKED 상태가 되면 락을 다시 획득하기 전까지는 계속 대기하고, CPU 실행 스케줄링에 들어가지 않는다.
  • t1 : 메서드 호출이 끝나면 락을 반납한다.
  • t2 : 인스턴스에 락이 반납되면 **락 획득을 대기하는 스레드는 자동으로 락을 획득**한다.
    • 이때 락을 획득한 스레드는 BLOCKED RUNNABLE 상태가 되고, 다시 코드를 실행한다.

락은 순서를 보장해주지는 않는다.

만약 BankAccount(x001) 인스턴스의 withdraw() 를 수 많은 스레드가 동시에 호출한다면, 1개의 스레드만 락을 획득하고 나머지는 모두 BLOCKED 상태가 된다. 그리고 이후에 BankAccount(x001) 인스턴스에 락을 반납하면, 해당 인스턴스의 락을 기다리는 수 많은 스레드 중에 하나의 스레드만 락을 획득하고, 락을 획득한 스레드만BLOCKED RUNNABLE 상태가 된다. 이때 어떤 순서로 락을 획득하는지는 자바 표준에 정의되어 있지 않다. 따라서 순서를 보장하지 않고, 환경에 따라서 순서가 달라질 수 있다.

실무에서는 어떻게 적용해야 할까?

synchronized 의 가장 큰 장점이자 단점은 한 번에 하나의 스레드만 실행할 수 있다는 점이다. 여러 스레드가 동시에실행하지 못하기 때문에, 전체로 보면 성능이 떨어질 수 있다. 따라서 synchronized 를 통해 여러 스레드를 동시에 실행할 수 없는 코드 구간은 꼭! 필요한 곳으로 한정해서 설정해야 한다. 그래도 성능을 고려한 설계 보다는 상대적으로 검증이 중요한 비즈니스 로직에서는 synchronized를 고려해볼만 한다.

Reference

김영한님의 자바 강의

https://stackoverflow.com/questions/20906548/why-is-synchronized-block-better-than-synchronized-method



© 2022. by taewoo

Powered by taewoo