CreatedDate: 2022년 10월 15일 오후 2:59
- 멀티 쓰레드가 공유자원을 접근하기 위해 경합하는 현상
@Test
void concurrency_stock_decrease() throws InterruptedException {
int threadCnt = 100;
ExecutorService executorService = Executors.newFixedThreadPool(32);
CountDownLatch latch = new CountDownLatch(threadCnt);
for (int i = 0; i < threadCnt; i++) {
executorService.submit(() -> {
try {
stockService.decrease(1L, 1L);
} finally {
latch.countDown();
}
});
}
latch.await();
Stock stock = stockRepository.findById(1L).orElseThrow();
assertEquals(0L, stock.getQuantity());
}
결과는 89
- 공유자원에 대한 쓰레드 동기화 (접근중이라면 대기)
@Transactional
public synchronized void decrease(Long id,Long quantity){
Stock stock=stockRepository.findById(id).orElseThrow();
stock.decrease(quantity);
stockRepository.saveAndFlush(stock);
}
결과 아직도 49
이유→ @Transactional은 새로운 클래스 작성
public class TransactionStockService {
private final StockService stockService;
public TransactionStockService(StockService stockService) {
this.stockService = stockService;
}
public void decrease(Long id, Long quantity) {
startTransaction(); // begin
stockService.decrease(id, quantity);
endTransaction(); // commit
}
}
다른 쓰레드가 이전 쓰레드가 endTransaction이 되기전에 decrease 메소드를 실행하여 반영이 안될 수 있음
@Transactional 어노테이션 삭제후 실행결과
-
Pessimistic Lock
(비관적락) -
Optimistic Lock
(낙관적락)- 실제 Lock을 이용하지 않고 Version을 이용함으로써 정합성을 맞춤
- Pessimistic Lock보단 성능상 이점이 존재
- but, 개발자가 재처리 로직을 짜줘야함
-
Named Lock
- 이름을 가진 mata-data locking방법
- 이름을 가진 Lock을 획득한 후 해제될때까지 다른 세션은 해당 Lock을 획득X
- Transaction이 종료될때 Lock이 자동해제되지 않음
- Lettuce
- Redisson
- 재시도가 필요하지 않은 lock은 lettuce 활용
- 재시도가 필요한 경우 redisson 활용
- 이미 MySQL을 사용하고 있다면 별도의 비용없이 구축가능
- Redis보다는 성능이 좋지않다.
- 사용하고 있지 않고 있다면 별도 구축해야함
- MySQL보다는 성능이 좋음