๐์๋ก
@Transactional ์ด๋ ธํ ์ด์ ๊ณผ synchronized์ ๋์์ ์ฌ์ฉํ๊ณ ์ถ์ ๊ฒฝ์ฐ๊ฐ ์์ ์ ์๋ค.
ํธ๋์ญ์ ๊ฒฉ๋ฆฌ์์ค๊ณผ ๋ณ๊ฐ๋ก ํด๋น ๋ฉ์๋๋ฅผ ๋๊ธฐํ๋ฅผ ์ ์ฉ์ํค๊ณ ์ถ์ ๋.
ํ์ง๋ง, ํ ๋ฉ์๋ ์์ ํด๋น Transactional, Synchronized๋ฅผ ๋์์ ์ ์ฉ ์
์ํ๋๋๋ก ์๋ํ์ง ์์ ์ ์๋ค.
๊ทธ ์ด์ ๋ฅผ ์์๋ณด์.
๐๋ณธ๋ก
์๋ ์์๋ ์คํ์ค๋ฒํ๋ก์ฐ๋ฅผ ๋ฒ์ญํ์ฌ ํ์ด์ ์ ๋ฆฌํ์ต๋๋ค.
๐คจ๋ญ๊ฐ ๋ฌธ์ ์ง?
์ผ๋จ Synchronized๋ฅผ ์ฌ์ฉํ๋ ์ด์ ๋ ํด๋น ๋ฉ์๋๋ฅผ ํ ์ฐ๋ ๋์์๋ง ๋๋ฆฌ๊ธฐ ์ํด์๋ค.
ํ์ง๋ง, ํธ๋์ญ์ ์ด ๊ฐ์ด ์ ์๊ฐ ๋์ด์๋ค๋ฉด ์ฒซ ๋ฒ์งธ ์ฐ๋ ๋๊ฐ ๋๋๊ธฐ ์ ๋ ๋ฒ์งธ ์ฐ๋ ๋๊ฐ ๋ฐ๋ํ ์๋ ์๋ค.
๊ทธ ์ด์ ๋ฅผ ์ดํด๋ณด์.
@Transactional
public synchronized void onRequest(Request request) {
if (request.shouldAddBook()) {
if (database.getByName(request.getBook().getName()) == null) {
database.add(request.getBook());
} else {
throw new Exception("Cannot add book - book already exist");
}
} else if (request.shouldRemoveBook()) {
if (database.getByName(request.getBook().getName()) != null) {
removeBook();
} else {
throw new Exception("Cannot remove book - book doesn't exist");
}
}
}
(ํด๋น ๋ฉ์๋๋ ์๋ฅผ ๋ณด์ฌ์ฃผ๊ธฐ ์ํ ์ฝ๋์ด๋ฉฐ, ์๋ฒฝํ์ง ์์ ์ฝ๋์ ๋๋ค.)
์ ๋ฉ์๋๋ ๋์ ๋ฐฉ์์ ์ด๋ ๋ค.
1. ์ฑ ์ ์ถ๊ฐํ๋๊ฐ?
1-1. ํด๋น ์ฑ ์ด๋ฆ์ด db์ ์๋๊ฐ?
1-2. ์์ผ๋ฉด ์ถ๊ฐ
1-3. ์๋ค๋ฉด Exception
2. ์ฑ ์ ์ญ์ ํ๋๊ฐ?
2-1. ํด๋น ์ฑ ์ด๋ฆ์ด db์ ์๋๊ฐ?
2-2. ์๋ค๋ฉด ์ญ์
2-3. ์๋ค๋ฉด Exception
๐ ์์
๋์ผํ ์ ๋ชฉ์ ์ฑ ์ ์ ๊ฑฐํ๊ณ ์ถ๊ฐํ๋ ์์๋ก ํด๋น ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค๊ณ ๊ฐ์ ํ๊ณ ์งํํด ๋ณด๊ฒ ๋ค.
1. Transactional, Synchronized ํค์๋๊ฐ ์๋ค๋ฉด ?
Thread1: |---remove book--->
Thread2: |---add book--->
์ด๋ ์ญ์ ๊ฐ ๋๊ธฐ ์ ์ ๊ฑฐ์ ๋์์ ์ถ๊ฐ ๋ฉ์๋๊ฐ ๋ฐ๋๋๋ฏ๋ก
๋์ผํ ์ด๋ฆ์ book์ด db์ ์กด์ฌํ๊ฑฐ๋ Exception์ด ๋ฐ์ํ ์ ์๋ค.
2. Synchronized๋ฅผ ์ถ๊ฐํ๋ฉด ์ด๋ป๊ฒ ๋์ํ๋๊ฐ ?
Thread1: |---remove book---> Thread2: |---add book--->
์์ ๊ฐ์ด ์ญ์ ๊ฐ ๋๋ ํ ๋ค์ ์ฐ๋ ๋๋ฅผ ํตํด์ ์ฑ ์ ์ถ๊ฐํ๊ฒ ๋๋ค.
์ฐ๋ฆฌ๊ฐ ์ํ๋ ๋ฐฉ์์ด๋ค.
ํ์ง๋ง, ์ฐ๋ฆฌ์๊ฒ ํธ๋์ญ์ ์ด ํ์ํ๋ค.
3. Transactional์ ์ ์ฉํ๋ค๋ฉด ?
@Transactional ์ด๋ ธํ ์ด์ ์ AOP๋ค. ๊ณ ๋ก ์๋ก์ด ํ๋ก์๋ฅผ ์์ฑํ๊ฒ ๋๋ค.
begin Transaction ---> method ---> commit Transaction
์์ ๊ฐ์ด ๋ฉ์๋๋ฅผ ๊ฐ์ธ ๋ฉ์๋ ์คํ ์ , ํ๋ก ์๋ก์ด ์ฝ๋๋ฅผ ํธ์ถํ๊ฒ ๋๋ค.
๊ทธ๋ ๋ค๋ฉด ์ ๊ฑฐ, ์ถ๊ฐ๋ฅผ ์งํํ ๋ ์ด๋ป๊ฒ ์ด๋ป๊ฒ ๋์์ ํ๊ฒ ๋ ๊น?
T1: |--Spring begins Transaction---> ---remove book---> ---Spring commits Transaction--->
T2: |--Spring begins Transaction---> ---add book---> ---Spring commits Transaction--->
ํธ๋์ญ์ ์ ์ ์ฉํ๊ฒ ๋๋ฉด ์ด๋ฌํ ๋ฐฉ์์ผ๋ก ๊ฑฐ์ ๋์์ ์งํ๋๊ฒ ๋๋ค.
์์ผ๋ก ์์ฝํ์ฌ ์๋์ ๊ฐ์ด ํ์ํ๊ฒ ์ต๋๋ค.
T1: |--B--|--R--|--C-->
T1: |--B--|--A--|--C-->
ํธ๋์ญ์ ์ ๋์ผํ entity๋ง์ ๋์์ ์์ ํ์ง ๋ชปํ๊ฒ ํ๋ค.
์๋ก add๋๋ book์ ๋ค๋ฅธ entity๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ์ ์์ ์ผ๋ก ์๋์ด ๋ ๊ฒ์ด๋ค.
ํ์ง๋ง, ๋์ผํ ์ด๋ฆ์ book์ด ๋์์ db์ ์ ์ฅ๋์ด ์๋ ์๊ฐ์ด ์กด์ฌํ๊ฑฐ๋ Exception์ด ๋ฐ์ํ ์ ์๋ค.
4. Synchronized๊ณผ Transactional์ ๋์์ ์ ์ฉํ๊ฒ ๋๋ฉด ?
์ฌ๊ธฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
Spring์์ ์ถ๊ฐํ๋ Transactional ์ฝ๋๋ ๋๊ธฐํ๋ก ๊ฐ์ธ์ง ์ฝ๋๊ฐ ์๋ ๋ณ๋์ ์ฝ๋๋ผ๋ ์ ์ด๋ค.
๊ทธ๋ก ์ธํด ๋๊ธฐํ๋ ๋ฉ์๋๊ฐ ์ข ๋ฃ๋ ํ commit ์ด ๋๊ธฐ ์ ์์ ์์ ๋ ๋ฒ์งธ ์ฐ๋ ๋๊ฐ ์งํ๋ ์ ์๋ค.
T1: |--B--|--R--|--C-->
T2: |--B---------|--A--|--C-->
์์ ๊ฐ์ด remove ๋ฉ์๋๊ฐ ๋๋ ์์ ์์ add ๋ฉ์๋๊ฐ ๋์ํ๊ฒ ๋๋ฉด
commits์ด ์ด๋ฃจ์ด์ง๊ธฐ ์ ์ add ๋ฉ์๋๊ฐ ์งํ๋๊ธฐ ๋๋ฌธ์ Exception์ด ๋ฐ์ํ ์ ์๋ค.
๐ค์ด๋ป๊ฒ ํด๊ฒฐํ์ง?
ํ์ฌ ์ฌ์ฉํ๊ณ ์๋ ๋ฐฉ์์ @Transactional์ ํธ์ถํ๊ธฐ ์ ์ ๋จผ์ Synchronized๋ฅผ ํธ์ถํ๋ ๋ฐฉ์์ด๋ค.
public class SynchronizedService() {
@Autowired
private TransactionService transactionService;
public synchronized void onRequest(Request request) {
transactionService.onRequest(request);
}
}
public class TransactionService() {
@Transactional
public void onRequest(Request request) {
if (request.shouldAddBook()) {
if (database.getByName(request.getBook().getName()) == null) {
database.add(request.getBook());
} else {
throw new Exception("Cannot add book - book already exist");
}
} else if (request.shouldRemoveBook()) {
if (database.getByName(request.getBook().getName()) != null) {
removeBook();
} else {
throw new Exception("Cannot remove book - book doesn't exist");
}
}
}
}
์์ ๊ฐ์ด synchronized ์์์ @Transactional ๋ฉ์๋๋ฅผ ํธ์ถํ๊ฒ ๋๋ฉด
์ฐ๋ฆฌ๊ฐ ์ํ๋ ๋ฐฉํฅ์ผ๋ก ์งํ๋๋ค.
T1: |--B--|--R--|--C-->T2: |--B--|--A--|--C-->
๊ฒ์ํด๋ณธ ๊ฒฐ๊ณผ ๊ฒฉ๋ฆฌ์์ค์ผ๋ก ํด๊ฒฐํ๋ผ๋ ๊ธ์ ๋ช๋ช ์ ํ๊ธด ํ์๋ค.
ํ์ง๋ง, ํธ๋์ญ์ ๊ฒฉ๋ฆฌ์์ค์ผ๋ก ์กฐ์ ํ๋ ๊ฒ์ ๋ฐ์ดํฐ์ ๋ฌด๊ฒฐ์ฑ์ ์ํด์์ด๋ค.
์ด๋ ๋ฐ์ดํฐ์ ๊ฒฉ๋ฆฌ์์ค์ ์กฐ์ ํ๋ ๊ฒ์ด์ง synchronized์๋ ์ ํ ๋ค๋ฅธ ์ ๊ทผ์ด๋ผ๊ณ ์๊ฐํ๋ค.
์ด๋์ ๋ ๊ฒฉ๋ฆฌ์์ค์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๊ถ ๋น์ทํ ํจ๊ณผ๋ฅผ ๋ผ ์ ์๊ฒ ์ง๋ง,
์ด๋ ๋ด๊ฐ ์ํ๋ ๋ฐฉํฅ๊ณผ๋ ๋ค๋ฅธ ๋ฐฉํฅ์ด๋ผ๊ณ ์๊ฐํ๊ณ ์๋ฒฝํ๊ฒ ๊ฐ์ ๋์์ ์ด๋ค๋ด์ง ์๋๋ค๊ณ ์๊ฐํ๋ค.
๋ ์ข์ ๋ฐฉ์์ด๋ ์๋ชป๋ ์ ์ด ์๋ค๋ฉด ์๋ ค์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค. (__)
์ฐธ์กฐ
https://stackoverflow.com/questions/41767860/spring-transactional-with-synchronized-keyword-doesnt-work
https://stackoverflow.com/questions/6479283/logical-comparison-of-java-synchronized-keyword-and-spring-transactional-annota
https://github.com/Gluewine/Gluewine/issues/25
'JVM > Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[SPRING] ์คํ๋ง์ ์ปจํธ๋กค๋ฌ๋ ์ด๋ป๊ฒ ์ฌ๋ฌ ์์ ์ ์ฒ๋ฆฌํ ๊น? (0) | 2021.11.15 |
---|---|
[SPRING] @Transactional readOnly ์ฑ๋ฅํฅ์ ์ด์ (0) | 2021.09.05 |
[SPRING] AOP ์ฐ์ ์์ ์ค์ ํ๊ธฐ. (0) | 2021.08.14 |
[SPRING] @Transactional (ํธ๋์ญ์ ) ๊ฐ์ ๋กค๋ฐฑ (0) | 2021.07.25 |
[SPRING] Spring AOP Proxy - @Transactonal ์ฌ์ฉ ์ ์ฃผ์์ฌํญ (0) | 2021.07.25 |