1. ๊ฐ์
ํ๋ก์ ํธ์์ ๋ค์๊ณผ ๊ฐ์ ๋ก์ง์ด ์๋ค. ๋ชจ์ง ๊ณต๊ณ ์ ๋ชจ์ง ์ธ์์ ์ด๊ณผํ์ง ์๋๋ค๋ฉด ๋ชจ์ง๋ ์ธ์์ ์ฆ๊ฐ์์ผ ์ฃผ๊ณ ์๋ค.
public synchronized void plusRegisteredNum(Integer updateCount) {
if (this.registeredNum + updateCount > this.recruitNum) {
throw new CustomException(ErrorCode.APPLY_OVER_RECRUIT_NUM);
}
this.registeredNum += updateCount;
}
๋์์ฑ ์ด์๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด synchronized ํค์๋๋ฅผ ๋ฃ์ด์ฃผ์๋๋ฐ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํด๋ดค์ ๋ ๋์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ณ ์์๋ค.
์์ธ์ ์ฐพ์๋ดค๋๋ @Transactional๊ณผ ํจ๊ป ์ฌ์ฉํ ์ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค๊ณ ํ๋ค.
์ ๊ทธ๋ฐ์ง ์์ธํ๊ฒ ์์๋ณด์.
2. ๋ฌธ์ ์ํฉ
@Test
public void ๋์์ฑ_ํ
์คํธ() throws InterruptedException {
int threadCount = 100;
ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
CountDownLatch latch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
executorService.submit(() -> {
try {
workDateTestService.plusRegisteredNum(1L);
} catch (Exception e) {
e.printStackTrace();
} finally {
latch.countDown();
}
});
}
latch.await();
executorService.shutdown();
WorkDate updatedWorkDate = workDateRepository.findById(1L).get();
assertThat(updatedWorkDate.getRegisteredNum()).isEqualTo(threadCount);
}
๋ชจ์ง ์ธ์์ ์ฆ๊ฐ์ํค๋ api์ ๋ก์ง์ ๋ง์ด ๋ณต์กํด ํด๋น ๊ธฐ๋ฅ๋ง ํ ์คํธ ํ๊ธฐ ์ํด TestService๋ฅผ ๋ง๋ค์ด์ฃผ์๋ค.
@Service
@RequiredArgsConstructor
@Transactional
public class WorkDateTestService {
private final WorkDateRepository workDateRepository;
public void plusRegisteredNum(Long workDateId) {
WorkDate workDate = workDateRepository.findById(workDateId)
.orElseThrow(() -> new CustomException(ErrorCode.WORK_DATE_NOT_FOUND));
workDate.plusRegisteredNum(1);
}
}
์ ํ ์คํธ ๊ฒฐ๊ณผ๋ ์๋์ ๊ฐ๋ค.
3. ๋ฌธ์ ์์ธ
synchronized๋ฅผ ์ฌ์ฉํ์์๋ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ง ๋ชปํ ์ด์ ๋ ํธ๋์ญ์ ์ ๋์์๋ฆฌ ๋๋ฌธ์ด๋ค.
์ฌ์ง๊ณผ ํจ๊ป ์์ธํ๊ฒ ์ดํด๋ณด์.
@Transaction์ด ์ ์ฉ๋ ํด๋์ค๋ CGLIB์ ์ํด์ ๋ฐํ์์ ํด๋น ํด๋์ค ๊ธฐ๋ฐ ํ๋ก์๊ฐ ์์ฑ๋๋ค.
๊ทธ๋ฆฌ๊ณ @Transactional ๋ก์ง์ผ๋ก ์ง์ ํ๊ธฐ ์ /ํ์์ Transaction Begin & Commit/Rollback์ด ์งํ๋๋ ๊ฒ์ด๋ค
์ด๋ ๊ฒ @Transactional์ด ๊ฑธ๋ ค์๋ ๋น์ฆ๋์ค ๋ก์ง์ synchronized ํค์๋๋ฅผ ๋ถ์ด๊ฒ ๋๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๋์ํ๋ค
ํด๋น ๋น์ฆ๋์ค ๋ก์ง์ synchronized๊ฐ ๊ฑธ๋ ค์์ผ๋ ํด๋น ๋ก์ง์ผ๋ก ์ง์ ํ ๋ Monitor Lock์ ๊ฐ์ง๊ณ ์ง์ ํ๊ฒ ๋๋๊ฒ์ด๋ค
๊ทธ๋ฌ๋ฉด Thread1์ ์ ์ธํ ๋๋จธ์ง ์ฐ๋ ๋๋ค์ ๋น์ฆ๋์ค ๋ก์ง์ ์ ๊ทผํ์ง ๋ชปํ๊ณ Lock์ ์ป๊ธฐ ์ํด์ ๋๊ธฐํ๋ค ์ฌ๊ธฐ์ Thread1์ด ๋น์ฆ๋์ค ๋ก์ง์ ๋๋ด๊ณ ์ปค๋ฐ/๋กค๋ฐฑ ์์ ์ผ๋ก ๋์ ํ๋ค๊ณ ๊ฐ์ ํ์
์ด ์์ ์ Thread2๊ฐ ์ง์ ํ๊ฒ ๋๋ฉด ์์ง Thread1์ ๋ก์ง์ด commit๋๊ธฐ ์ ์ด๋ฏ๋ก DB์ ์กด์ฌํ๋ Ticket์ stock์ ์ฌ์ ํ 100์ด๋ค.
๊ทธ์ ๋ฐ๋ผ์ Thread2๋ Ticket์ stock์ 100์ผ๋ก ๋ฐ๊ฒ ๋๊ณ ๊ทธ์ ๋ฐ๋ฅธ ๋ก์ง์ด ์งํ๋๋ค.
์ฆ ํธ๋์ญ์ ์ด ์์ & ์ปค๋ฐ,๋กค๋ฐฑ ๋๋ ์์ ์ Lock์ด ๊ฑธ๋ฆฌ์ง ์๊ฒ ๋๊ณ ์ฌ๊ธฐ์ ๋์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ฒ ๋๋ ๊ฒ์ด๋ค.
4. ๊ฒฐ๋ก
synchronized ํค์๋๋ Java์์ ์ค๋ ๋ ๋๊ธฐํ๋ฅผ ์ ๊ณตํ๋ ๊ฐ๋ ฅํ ๋๊ตฌ์ด์ง๋ง, ํ๊ณ๊ฐ ๋ช ํํ ๊ฒ ๊ฐ๋ค.
์ฒซ ๋ฒ์งธ๋ก ํธ๋์ญ์ ๊ณผ ํจ๊ป ์ฌ์ฉํ ์ ์๋ ๊ฒ์ด๋ค. ํจ๊ป ์ฌ์ฉํ๋ ค๋ฉด ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ฆฌํ๊ณ ..~~ ํด์ ์ฌ์ฉํ ์ ์์ง๋ง ์ฝ๋ ๊ฐ๋ ์ฑ๋ ๋จ์ด์ง ๋ฟ๋๋ฌ ํจ์จ์ ์ด์ง ๋ชปํ๋ค๋ ์๊ฐ์ด ๋ค์๋ค.
๋๋ฒ์งธ๋ก ๋ชจ๋ ์ค๋ ๋ ๋์์ ๋ํด ๋ฝ์ ๊ฑธ์ด๋ฒ๋ ค ์ค๋ฒํค๋๊ฐ ๋ฐ์ํ ์ ์๋ค.
์ธ๋ฒ์งธ๋ก ํ๋์ ํ๋ก์ธ์ค์์๋ง ๊ฒฉ๋ฆฌ์ฑ์ ๋ณด์ฅํ๊ธฐ ๋๋ฌธ์ ๋ ๊ฐ ์ด์์ ์๋ฒ์์๋ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.