프록시 객체가 생성될 때 synchronized 없이 메서드가 생성되는 것이 맞을까요?
먼저 좋은 강의 감사드립니다.
프록시 객체에서 이해가 안 가는 부분이 있어 질문드립니다.
@Transactional을 사용하면 프록시 방식의 AOP로 동작하는 것은 이해하고 있습니다.
스프링 부트는 CGLIB 방식으로 프록시 객체를 생성하므로, StockService를 상속하는 StockServiceProxy가 만들어질 때 StockServiceProxy.decrease()에도 synchronized 키워드가 붙어있을 것이라고 생각했습니다.
그런데 강사님께서 TransactionStockService 를 예로 드실 때 synchronized 를 안 붙이신 걸 보니 프록시 객체가 생성될 때는 synchronized 가 안 붙는 건가? 라고 생각들었습니다.
Q. 프록시 객체가 생성될 때 synchronized 없이 메서드가 생성되는 것이 맞을까요?
읽어주셔서 감사합니다 :)
답변 1
1
HRO 님 안녕하세요.
프록시 객체가 생성될때는 synchronized 가 붙여진채로 생성이 됩니다.
TransactionStockService 는 StockService 의 Proxy 객체를 구현한것이 아니라 Transactional 어노테이션을 붙였을 때 저러한 방법으로 동작한다는 것을 쉽게 말씀드리기 위해 작성한 클래스입니다.
감사합니다.
0
강사님 빠른 답변 감사합니다.
조금 오래된 글이지만, 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 가 붙여진채로 메서드가 생성된다면, 강의에서 TransactionStockService의 stockService.decrease() 와 endTransaction 사이에 다른 스레드가 접근하는 것이 아니라 endTransaction 이 끝나고 DB 트랜잭션이 커밋되는 사이에 다른 스레드가 접근해서 갱신 손실 문제가 일어날 것 같습니다. 제가 이해한 것이 맞을까요?
긴 글 읽어주셔서 감사합니다!
1
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) {
// ....
}
}추가적인 질문이 있으시다면 답글 남겨주세요!
감사합니다 :)
레디선 테스트코드에서 채널이름은 없어도 되는건가요?
0
37
2
낙관적 락을 사용할 떄 차이점
0
117
2
동시성 검증 코드에 관한 문의
0
88
2
단일연산
0
68
2
낙관적락vs. 레디스락
0
103
2
안녕하세요. 레디슨 질문있습니다..!!
0
65
2
@Lock(OPTIMISTIC)이 필요한 이유
0
88
2
get_lock 의 timeout이 3000초 이던데 너무 긴거 아닌가요?
0
128
2
DataSource Hikari 사용 이유
0
142
2
saveAndFlush 사용 이유 문의
0
111
3
비관적 락 VS 네임드 락
0
155
3
application.yaml 에 redis 정보
0
96
2
왜 클래스 이름에 Facade 가 붙나요?
0
180
2
@Transactional 으로 인한 동시성 문제 발생 원인이 궁금합니다.
0
215
2
@modifying 이용한 동시성 제어
0
166
2
DB락과 분산락
0
258
2
NamedLock 테스트 실패
0
186
2
테스트에서 트랜잭션 어노테이션 질문 있습니다.
0
168
2
optimistic Lock 재시도 질문입니다.
0
229
2
낙관적 락 테스트 실패
0
238
2
오류?
0
1622
4
LettureLockStockFacadeTest에서 오류가 발생합니다.
1
264
2
Pessimistic Lock 전체 테스트 오류 문의
0
353
3
비관적 락 vs 레디스(Lettuce)락 비교 관련 질문
0
453
2





