인프런 커뮤니티 질문&답변

eomukeomukeomuk님의 프로필 이미지

작성한 질문수

재고시스템으로 알아보는 동시성이슈 해결방법

Synchronized 이용해보기

프록시 객체가 생성될 때 synchronized 없이 메서드가 생성되는 것이 맞을까요?

해결된 질문

23.06.17 20:28 작성

·

447

1

먼저 좋은 강의 감사드립니다.

프록시 객체에서 이해가 안 가는 부분이 있어 질문드립니다.

@Transactional을 사용하면 프록시 방식의 AOP로 동작하는 것은 이해하고 있습니다.

스프링 부트는 CGLIB 방식으로 프록시 객체를 생성하므로, StockService를 상속하는 StockServiceProxy가 만들어질 때 StockServiceProxy.decrease()에도 synchronized 키워드가 붙어있을 것이라고 생각했습니다.

그런데 강사님께서 TransactionStockService 를 예로 드실 때 synchronized 를 안 붙이신 걸 보니 프록시 객체가 생성될 때는 synchronized 가 안 붙는 건가? 라고 생각들었습니다.

Q. 프록시 객체가 생성될 때 synchronized 없이 메서드가 생성되는 것이 맞을까요?

읽어주셔서 감사합니다 :)

 

답변 1

1

최상용님의 프로필 이미지
최상용
지식공유자

2023. 06. 18. 18:34

HRO 님 안녕하세요.
프록시 객체가 생성될때는 synchronized 가 붙여진채로 생성이 됩니다.
TransactionStockService 는 StockService 의 Proxy 객체를 구현한것이 아니라 Transactional 어노테이션을 붙였을 때 저러한 방법으로 동작한다는 것을 쉽게 말씀드리기 위해 작성한 클래스입니다.

감사합니다.

eomukeomukeomuk님의 프로필 이미지

2023. 06. 18. 19:17

강사님 빠른 답변 감사합니다.

조금 오래된 글이지만, synchronized 상속과 관련하여 다음 글을 구글링하였습니다.

The synchronized keyword is not considered to be part of a method's signature. So the synchronized modifier is not automatically inherited when subclasses override superclass methods, and methods in interfaces cannot be declared as synchronized. - https://gee.cs.oswego.edu/dl/cpj/mechanics.html

위 글에서는 결국 synchronized 는 메서드 시그니처가 아니므로, 오버라이딩 할 때 자동으로 상속되지 않는다고 얘기하고 있습니다.

강사님 말씀처럼 synchronized 가 붙여진 채로 생성된다면,

StockServiceProxy.decrease()는 다음과 같이 생겼을 걸로 예상됩니다.

@Override
public synchronized void decrease(Long id, Long quantity) {
    try{
        tx.start();
        stockService.decrease();
    } catch (Exception e) {
         // ...
    } finally {
        tx.commit();
    }
}

만약 synchronized 가 붙여진 채로 생성된다면, tx.commit() 이 호출되고 실제 DB에 커밋되기 전에 다른 스레드가 접근해서 커밋되기 이전의 quantity를 읽어서 갱신 손실 문제가 발생했다고 보면 될까요?

강의에서는 stockService.decrease() 만 synchronized의 적용을 받아, stockService.decrease()tx.commit() 사이에 다른 스레드가 quantity에 접근하여
갱신손실 문제가 일어난 것으로 설명해주셨습니다.

Q1. synchronized 는 원래 상속이 안 되는데 Proxy 객체 생성 시에만 synchronized 가 상속 되는 건가요?
Q2. Proxy 객체 생성 시에 synchronized 가 붙여진채로 메서드가 생성된다면, 강의에서 TransactionStockServicestockService.decrease()endTransaction 사이에 다른 스레드가 접근하는 것이 아니라 endTransaction 이 끝나고 DB 트랜잭션이 커밋되는 사이에 다른 스레드가 접근해서 갱신 손실 문제가 일어날 것 같습니다. 제가 이해한 것이 맞을까요?

긴 글 읽어주셔서 감사합니다!

 

최상용님의 프로필 이미지
최상용
지식공유자

2023. 06. 18. 19:29

HRO 님 안녕하세요.
저의 착각으로 전달을 잘못드린것 같습니다.
전달을 드리고자 했던 내용은 "synchronized 를 보장한다" 였습니다!
synchronized 는 구현되지 않는것이 맞으며 아래와 같은 매커니즘으로 동작합니다.

// Proxy class
class TransactionStockService {

    private StockService stockService;

    public void decrease(Long id, Long quantity) {
         try{
             tx.start();
             stockService.decrease();
         } catch (Exception e) {
             // ....
         } finally {
             tx.commit();
         }
    }
}

// Origin Class
class StockService {
    public synchronized void decrease(Long id, Long quantity) {
        // ....
    }
}

추가적인 질문이 있으시다면 답글 남겨주세요!
감사합니다 :)