Java Adv 05 - Synchronized Method
in Programming Language on Java
Synchronized로 동시성 제어하기
- 우선 코드의 결과부터 보면 t1의 작업이 모두 수행이 되고(검증, 결과) 이 후에 t2의 작업이 수행이 된다.
- 따라서 t1,t2가 동시에 출금하지 않아서 클라이언트는 동시성 오류를 피할 수 있게 되었다.
- synchronized는 도대체 뭘까?
모든 객체(인스턴스)는 내부에 자신만의 락(lock )을 가지고 있다.
- 모니터 락(monitor lock)이라도고 부른다.
- 객체 내부에 있고 우리가 확인하기는 어렵다.
- 스레드가
synchronized키워드가 있는 메서드에 진입하려면 반드시 해당 인스턴스의 락이 있어야 한다! - 여기서는
BankAccount(x001)인스턴스의synchronized withdraw()메서드를 호출하므로 이 인스턴스의 락이 필요하다. 스레드t1,t2는withdraw()를 실행하기 직전이다.
Synchronized 키워드의 동작
- t1이 먼저 실행된다고 가정한다.
- 스레드
t1이 먼저synchronized키워드가 있는withdraw()메서드를 호출한다. synchronized메서드를 호출하려면 먼저 해당 인스턴스의 락이 필요하다.- 락이 있으므로 스레드
t1은BankAccount(x001)인스턴스에 있는 락을 획득한다. - 스레드
t1은 해당 인스턴스의 락을 획득했기 때문에withdraw()메서드에 진입할 수 있다. - 스레드
t2도withdraw()메서드 호출을 시도한다.synchronized메서드를 호출하려면 먼저 해당 인스턴스의 락이 필요하다.(언젠가는 락이 오겠지..) - 스레드
t2는BankAccount(x001)인스턴스에 있는 락 획득을 시도한다. 하지만 락이 없다. - 락이 없으면
t2스레드는 락을 획득할 때 까지BLOCKED상태로 대기한다.t2스레드의 상태는RUNNABLEBLOCKED상태로 변하고, 락을 획득할 때 까지 무한정 대기한다. 참고로BLOCKED상태가 되면 락을 다시 획득하기 전까지는 계속 대기하고, CPU 실행 스케줄링에 들어가지 않는다. t1: 메서드 호출이 끝나면 락을 반납한다.t2: 인스턴스에 락이 반납되면 **락 획득을 대기하는 스레드는 자동으로 락을 획득**한다.- 이때 락을 획득한 스레드는
BLOCKEDRUNNABLE상태가 되고, 다시 코드를 실행한다.
- 이때 락을 획득한 스레드는
락은 순서를 보장해주지는 않는다.
만약 BankAccount(x001) 인스턴스의 withdraw() 를 수 많은 스레드가 동시에 호출한다면, 1개의 스레드만 락을 획득하고 나머지는 모두 BLOCKED 상태가 된다. 그리고 이후에 BankAccount(x001) 인스턴스에 락을 반납하면, 해당 인스턴스의 락을 기다리는 수 많은 스레드 중에 하나의 스레드만 락을 획득하고, 락을 획득한 스레드만BLOCKED RUNNABLE 상태가 된다. 이때 어떤 순서로 락을 획득하는지는 자바 표준에 정의되어 있지 않다. 따라서 순서를 보장하지 않고, 환경에 따라서 순서가 달라질 수 있다.
실무에서는 어떻게 적용해야 할까?
synchronized 의 가장 큰 장점이자 단점은 한 번에 하나의 스레드만 실행할 수 있다는 점이다. 여러 스레드가 동시에실행하지 못하기 때문에, 전체로 보면 성능이 떨어질 수 있다. 따라서 synchronized 를 통해 여러 스레드를 동시에 실행할 수 없는 코드 구간은 꼭! 필요한 곳으로 한정해서 설정해야 한다. 그래도 성능을 고려한 설계 보다는 상대적으로 검증이 중요한 비즈니스 로직에서는 synchronized를 고려해볼만 한다.
Reference
김영한님의 자바 강의