Java ‐ 동시성 이슈를 Synchronized로 해결하기 - dnwls16071/Backend_Study_TIL GitHub Wiki

📚 작성한 재고 감소 테스트 코드

@SpringBootTest
class StockServiceTest {

	@Autowired private StockService stockService;
	@Autowired private StockRepository stockRepository;

	@BeforeEach
	public void before() {
		stockRepository.saveAndFlush(new Stock(1L, 100L));
	}

	@AfterEach
	public void after() {
		stockRepository.deleteAll();
	}

	@Test
	@DisplayName("상품 주문시 수량만큼의 재고 감소가 이루어져야 한다.")
	void 재고_감소() {
		stockService.decrease(1L, 1L);

		Stock stock = stockRepository.findById(1L).orElseThrow();

		assertThat(stock.getQuantity()).isEqualTo(99L);
	}

	@Test
	@DisplayName("동시에 여러 요청을 통한 상품 주문시 요청만큼의 재고 감소가 이루어진다.")
	void 동시에_여러_요청을_통한_상품_주문시_요청만큼의_재고_감소가_이루어진다() throws InterruptedException {
		int threadCount = 100;
		ExecutorService executorService = Executors.newFixedThreadPool(32);
		CountDownLatch latch = new CountDownLatch(threadCount);
		for (int i = 0; i < threadCount; i++) {
			executorService.submit(() -> {
				try {
					stockService.decrease(1L, 1L);
				} finally {
					latch.countDown();
				}
			});
		}
		latch.await();
		Stock stock = stockRepository.findById(1L).orElseThrow();

		/*
		 * @Transactional 어노테이션과 synchronized 키워드를 같이 사용하는 경우 : 동시성 문제 발생
		 * synchronized 키워드를 사용하는 이유 : 한 쓰레드에 대해서만 사용하기 위해서
		 * 하지만 여기에 @Transactional 어노테이션을 사용하는 경우 트랜잭션이 끝나기 전에 다른 쓰레드에서 접근할 수 있어 문제가 발생한다.
		 */
		assertThat(stock.getQuantity()).isEqualTo(0L);
	}
}