-
[Thread] ๋์์ฑ ์ด์(Synchronization Issue), ์ฐ๋ ๋๋ก์ปฌ(ThreadLocal), ์ฐ๋ ๋ํ(ThreadPool), ์ฐ๋ ๋์ ์์ ํ ์ค๊ณ(ThreadSafe)์ ๋ํ ๊ณต๋ถ ๊ธฐ๋ก๊ฐ๋ฐ/Java 2022. 4. 23. 21:54
๐ก๋์์ฑ ์ด์๋ ๋ฌด์์ธ๊ฐ?
๋์์ฑ ์ด์๋ ์ฌ๋ฌ ์ฐ๋ ๋๊ฐ ๋์์ ๊ฐ์ ์ธ์คํด์ค์ ํ๋ ๊ฐ์ ๋ณ๊ฒฝํ๋ฉด์ ๋ฐ์ํ๋ ๋ฌธ์ ๋ฅผ ์๋ฏธํ๋ค.
๊ฐ์ฅ ๋ํ์ ์ธ ์๋ก ์ฑ๊ธํค(Singleton) ํจํด์ ์๋ก ๋ค ์ ์๋ค.
โ ์ฑ๊ธํค(Singleton) ํจํด์ด๋?
์ฑ๊ธํค์ด๋ ์ด๋ค ํด๋์ค๊ฐ ์ต์ด ํ๋ฒ๋ง ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ ๋นํ๊ณ (Static) ๊ทธ ๋ฉ๋ชจ๋ฆฌ์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๋ ๋์์ธ ํจํด์ ์๋ฏธํ๋ค. ์ฆ, ์์ฑ์์ ํธ์ถ์ด ๋ฐ๋ณต์ ์ผ๋ก ์ด๋ค์ ธ๋ ์ค์ ๋ก ์์ฑ๋๋ ๊ฐ์ฒด๋ ์ต์ด ์์ฑ๋ ๊ฐ์ฒด๋ฅผ ๋ฐํ ํด์ฃผ๋ ๊ฒ์ด๋ค.
์๋์ ์ฝ๋๋ฅผ ๋ณด์.
@Service public class UserService { private static UserId userId; // ์ํ ๊ฐ public void createUser(User user) { // ์ฌ๊ธฐ์ userId ๊ฐ์ ๋ณ๊ฒฝํ๋ ์ฝ๋๊ฐ ์กด์ฌ } }
UserService ๋ ์ฑ๊ธํค ๊ฐ์ฒด๋ก ๋ฑ๋ก๋๋ ๋น์ด๋ค. ์ ์ฝ๋์์ UserId๋ผ๋ ๊ฐ์ฒด๊ฐ ์ํ ๊ฐ์ธ๋ฐ, ํ์ ๊ฐ์ 100๊ฑด์ด ๋์์ ์ด๋ฃจ์ด์ก๋ค๊ณ ํ๋ฉด userId ๋ผ๋ ์ํ๊ฐ์ ์์ ํ ๊น? ์์ ํ์ง ์์๊น?
์ฐ๋ ๋๋ ํ๋ก์ธ์ค์ Stack, Heap, Data, Text ์์ญ ์ค Stack ๋ถ๋ถ์ ์ ์ธํ ๋๋จธ์ง๋ฅผ ๊ณต์ ํ๋ค. ๊ทธ๋ฆฌ๊ณ userId ์ ์ํ ๊ฐ์ ์ ์ญ๋ณ์์ด๋ฉฐ Process์ Data ์์ญ์ ์ ์ฌ๋๋ค. ์ฆ, ์ฐ๋ฆฌ๊ฐ ๊ฐ๋ฐํ๋ ๋ฉํฐ ์ฐ๋ ๋ ํ๊ฒฝ์์ ์ ๋ง์ ์ฐ๋ ๋๋ค์ด userId ๋ผ๋ ์ํ ๊ฐ(์ ์ญ ๋ณ์)์ ๊ณต์ ํ๊ฒ ๋๋ ๊ฒ์ด๋ค. ๊ทธ๋ ๊ฒ๋๋ฉด, ํ์ ๊ฐ์ ํ์ ์๋ก ๋ค๋ฅธ ์ฌ๋์ ๊ฐ์ธ์ ๋ณด๊ฐ ๋ณด์ธ๋ค๋ ๋ฑ, ์ฌ๊ฐํ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค. ๋ฐ๋ผ์, ์์ ๊ฐ์ ์ฝ๋๋ ์ ๋ ์ง๋ฉด ์๋๋ค. ๋น๋ก ํธ๋ํฝ์ด ๋ฎ์ ์ ํ๋ฆฌ์ผ์ด์ ์ผ์ง๋ผ๋ ๋ง์ด๋ค.
๊ทธ๋ฌ๋ฉด ๋์์ฑ ์ด์๋ฅผ ํผํ๊ธฐ ์ํด์ ์ด๋ป๊ฒ ํด์ผํ ๊น?
์ด๊ฒ์ ๋ํ ํด๋ต๋ ์ญ์ ํ๋ก์ธ์ค์ ์ฐ๋ ๋์ ์๋ค. ์ฐ๋ ๋์ Stack ์์ญ์ ๊ณต์ ํ์ง ์์ผ๋ฉฐ, ์ง์ญ ๋ณ์๊ฐ ์ ์ฅ๋๋ค. ๋ฐ๋ผ์, ์ ์ญ ๋ณ์๋์ ์ง์ญ ๋ณ์๋ฅผ ์ฐ๊ฒ๋ ํ๋ฉด ์ ์ ๋ ธ๋ ฅ์ผ๋ก ๋์์ฑ ๋ฌธ์ ๋ฅผ ํผํ ์ ์๋ค.
๋์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ ๊ณณ์ ๊ฐ์ ์ธ์คํด์ค์ ํ๋, ๋๋ static ๊ฐ์ ๊ณต์ฉ ํ๋์ ์ ๊ทผํ ๋ ๋ฐ์ํ๋ค. ๋์์ฑ ๋ฌธ์ ๋ ๊ฐ์ ์ฝ๊ธฐ๋ง ํ๋ ๊ฒฝ์ฐ์๋ ๋ฐ์ํ์ง ์๊ณ , ์ด๋์ ๊ฐ ๊ฐ์ ๋ณ๊ฒฝํ๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ๋ ๋ฌธ์ ์ด๋ค.
๐กThreadLocal ์ด๋ ๋ฌด์์ธ๊ฐ?
ํ์ง๋ง, ์ฑ๊ธํค ๊ฐ์ฒด์์ ์ํ ๊ฐ์ ๊ฐ์ ธ์ผํ๋ ๊ฒฝ์ฐ๊ฐ ์์ ์ ์๋๋ฐ, ์ด๋๋ ์ด๋ป๊ฒ ๋์์ฑ ์ด์๋ฅผ ํผํ ๊น?
์ฑ๊ธํค ๋น์์ ์ํ๊ฐ์ ์ ์งํด์ผํ๋ ๊ฒฝ์ฐ์ ๋์์ฑ ์ด์๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ ThreadLocal์ ์ง์ํ๋ค.
ThreadLocal ์ ํด๋น ์ฐ๋ ๋๋ง ์ ๊ทผํ ์ ์๋ ํน๋ณํ ์ ์ฅ์(๋ณ๋์ ๋ด๋ถ ์ ์ฅ์๋ฅผ ์ ๊ณต)๋ฅผ ์๋ฏธํ๋ค. ๋ฐ๋ผ์, ์ฌ๋ฌ ์ฐ๋ ๋๊ฐ ๋์์ ์ํ ๊ฐ์ ์ ๊ทผํด๋ ๋์์ฑ ์ด์๊ฐ ๋ฐ์ํ์ง ์๋๋ค.
์ ์ฝ๋์ ThreadLocal ์ ์ ์ฉํด๋ณด์.
@Service public class UserService { private static final ThreadLocal<UserId> userIdStore = new ThreadLocal<>(); public void createUser(User user) { userIdStore.set(user.createUserId()); } public static UserId get() { return userIdStore.get(); } public static void remove() { userIdStore.remove(); } }
์ ์ฒ๋ผ ThreadLocal ์ ์ ์ฉํ์ฌ ์ฌ์ฉํ๋ฉด, ๊ฐ ์ฐ๋ ๋๋ UserId ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด ์์ ๋ง์ ๋ณ๋์ ๋ด๋ถ ์ ์ฅ์์์ ๊บผ๋ด๊ธฐ ๋๋ฌธ์ ๋์์ฑ ์ด์๋ก๋ถํฐ ์์ ํ๋ค.
๐กThreadLocal์ ์ ์ฅ๋ ๊ฐ์ ์ ๊ฑฐํ์ง ์์ผ๋ฉด ์ด๋ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ ๊น?
ThreadLocal ์ ์ฌ์ฉํ ๋์ ์ฃผ์์ ์ ํด๋น ์ฐ๋ ๋๊ฐ ์ฐ๋ ๋ ๋ก์ปฌ์ ๋ชจ๋ ์ฌ์ฉํ๊ณ ๋๋ฉด ThreadLocal.remove() ๋ฅผ ํธ์ถํด์ ์ฐ๋ ๋ ๋ก์ปฌ์ ์ ์ฅ๋ ๊ฐ์ ์ ๊ฑฐํด์ฃผ์ด์ผ ํ๋ค.
์ ๊ฑฐํ์ง ์์ผ๋ฉด ํน์ ์ํฉ์์ ๋ฉ๋ชจ๋ฆฌ ๋์, ๊ฐ์ธ์ ๋ณด ๋ฑ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค. ํนํ WAS ์ฒ๋ผ ์ฐ๋ ๋ ํ(Thread Pool)์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์ ์ฌ๊ฐํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
์ฐ๋ ๋ ํ์ ์์ฒญ ๋ง๋ค ์ฐ๋ ๋๋ฅผ ์์ฑํ์ ๊ฒฝ์ฐ์ ๋จ์ ์ ๋ณด์ํ๊ธฐ ์ํด ๋ง๋ค์ด์ก๋ค๊ณ ์์์ ๋ฐฐ์ ๋ค. ๋ฐ๋ผ์, WAS ๋ ์ฐ๋ ๋ ์ฌ์ฉ์ด ๋๋๋ฉด ํด๋น ์ฐ๋ ๋๋ฅผ ๋ค์ ๋ฐ๋ฉํ๋ค.
A ์ฌ์ฉ์๊ฐ ํ์ ๊ฐ์ ์์ฒญ์ ํ์ฌ thread-A ๊ฐ ํ ๋น๋์๋ค. thread-A ๊ฐ ์ฌ์ฉ์์ A ์ ๋ฐ์ดํฐ(User)๋ฅผ ์์ ์ ์ ์ฉ ๋ณด๊ด์์ธ thread-A-privacy-storage ์ ์ ์ฅํ๋ค. A ์ ์์ฒญ์ด ๋๋๊ณ ๋๋ฉด WAS ๋ ์ฌ์ฉ์ด ๋๋ thread-A ๋ฅผ ์ฌ์ฌ์ฉํ๊ธฐ ์ํด์ ์ฐ๋ ๋ ํ์ ๋ฐํํ๋ค. ๋ฐ๋ผ์, thread-A ๊ฐ ์ ๊ฑฐ๋์ง ์์์ผ๋ฏ๋ก thread-A-privacy-storage ๋ ์ด์์๊ฒ๋๋ค.
B ์ฌ์ฉ์๊ฐ ์์ ์ ์ ๋ณด๋ฅผ ์กฐํํ๊ธฐ ์ํ ์์ฒญ์ ํ๋ค. WAS ๋ ์ฐ๋ ๋ ํ์์ ๋จ๋ ์ฐ๋ ๋๋ฅผ ํ ๋นํด์ฃผ๋๋ฐ, ์ด๋ ์ด๋ค ์ฐ๋ ๋๊ฐ ํ ๋น๋ ์ง๋ ๋ชจ๋ฅธ๋ค. thread-A ๊ฐ ํ ๋น๋ ์๋ ์๊ณ , ๋ค๋ฅธ ์ฐ๋ ๋๊ฐ ํ ๋น๋ ์ ๋ ์๋ค. thread-A ๊ฐ ํ ๋น๋๋ฉด thread-A-privacy-storage ์ ๋ค์ด์๋ ์ฌ์ฉ์ A ์ ๋ํ ๊ฐ์ธ์ ๋ณด๊ฐ ์กฐํ๋๋ค. ๋ฐ๋ผ์, ๋ค๋ฅธ ์ฌ๋์ ๊ฐ์ธ์ ๋ณด๊ฐ๋ ธ์ถ ๋๋ ์ฌ๊ฐํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
๊ทธ๋ ๊ฒ ๋๋ฌธ์ ThreadLocal ์ ์ฌ์ฉํ๊ณ ๋์ ThreadLocal.remove() ๋ฅผ ํตํด ์์ ์ ์ ์ฉ ๋ณด๊ด์์ ์ ์ฅ๋์ด ์๋ ๊ฐ๋ค์ ๋ชจ๋ ์ ๊ฑฐํด์ฃผ์ด์ผ ํ๋ค.
โ ์ ๋ฆฌ
- ThreadLocal์ ์ ์ฅ๋ ๊ฐ์ ํด๋น ์ฐ๋ ๋์์ ๊ณ ์ ํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
- ThreadLocal ํด๋์ค์ ๋ณ์๋ private static final๋ก ์ ์ธํ๋ค.
- ThreadLocal ํด๋์ค์ ์ ์ธ๋์ด ์๋ ๋ฉ์๋๋ set(), get(), remove(), initialValue()๊ฐ ์๋ค.
- ์ฌ์ฉ์ด ๋๋ ํ์๋ remove() ๋ฉ์๋๋ฅผ ํธ์ถํด์ฃผ๋ ์ต๊ด์ ๊ฐ์ ธ์ผ๋ง ํ๋ค.์ฐธ๊ณ ๋ก ์์ ๋ฐ์ค์์ 3๋ฒ์งธ๋ฅผ ๋ณด๋ฉด, initialValue()๋ผ๋ ๋ฉ์๋๊ฐ ์๋๋ฐ, ์ด๊ฒ์ ThreadLocal ํด๋์ค๋ฅผ ํ์ฅํ์ฌ ์ฌ์ฉํ๋ ค๊ณ ํ ๋, ์ด ๋ฉ์๋๋ฅผ Overrideํ์ฌ ์ด๊ธฐ๊ฐ์ ์ง์ ํ ์ ์๋ค.
๐ก์ฐ๋ ๋ํ(ThreadPool)์ ๋ฌด์์ธ๊ฐ?
์น ๊ธฐ๋ฐ์ ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ์ฐ๋ ๋๋ฅผ ์ฌ์ฌ์ฉํ๊ธฐ ์ํด์ ์ฐ๋ ๋ ํ(Thread Pool)์ด๋ผ๋ ๊ฒ์ ์ฌ์ฉํ๋ค.
WAS(Web Application Server)์ ์ฐ๋ ๋ ํ์ ๋ค์๊ณผ ๊ฐ์ด ๋์ํ๋ค.
1. ํ(Pool)์์ ๋ฏธ๋ฆฌ ์ฐ๋ ๋๋ฅผ ๋ง๋ค์ด ๋๋๋ค.
2. ์ฌ์ฉ์ ์์ฒญ์ด ์ค๋ฉด ์ฐ๋ ๋ ํ์๊ฒ ๋๊ณ ์๋ ์ฐ๋ ๋๋ฅผ ๋ฌ๋ผ๊ณ ์์ฒญํ๋ค.
3. ์ฐ๋ ๋ ํ์์ ์ฐ๋ ๋๋ฅผ ๊ฐ์ ธ๋ค ์ด๋ค.
4. ์ฐ๋ ๋ ์ฌ์ฉ์ด ๋๋๋ฉด ๋ค์ ์ฐ๋ ๋ ํ์๊ฒ ๋ฐ๋ฉ์ ํ๋ค.์ฆ, ์ฐ๋ ๋๋ฅผ ์ฐ๋ ๋ ํ์์ ๋น๋ ค ์ฐ๊ณ , ๋ค์ ๋ฐ๋ฉํ๋ ์์ด๋ค. ์ฐ๋ ๋ ํ์์ ์ฐ๋ ๋๊ฐ 500 ๊ฐ์ด๋ฉฐ, 500 ๊ฐ๋ฅผ ๋๋ ์์ฒญ์ด ์ค๋ฉด, ์ฐ๋ ๋ ๋๊ธฐ, ๊ฑฐ์ ๋ฑ์ ํ ์ ์๋ค.
์ฐ๋ ๋๊ฐ ๋ฏธ๋ฆฌ ์์ฑ๋์ด ์์ผ๋ฏ๋ก, ์ฐ๋ ๋๋ฅผ ์์ฑํ๊ณ ์ข ๋ฃํ๋ ๋น์ฉ(CPU)์ด ์ ์ฝ๋๊ณ , ์๋ต์๊ฐ์ด ๋น ๋ฅด๋ค. ๋ํ ์์ฑ ๊ฐ๋ฅํ ์ฐ๋ ๋์ ์ต๋์น๊ฐ ์์ผ๋ฏ๋ก ๋๋ฌด ๋ง์ ์์ฒญ์ด ๋ค์ด์๋ ๊ธฐ์กด ์์ฒญ์ ์์ ํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค.
๐ก๊ทธ๋ ๋ค๋ฉด Thread Safeํ๊ฒ ์ค๊ณํ๋ ๋ฐฉ๋ฒ์ ๋ฌด์์ผ๊น?
Thread Safe ๋ ์ฝ๊ฒ ๋งํ๋ฉด ๋์์ฑ ์ด์๊ฐ ๋ฐ์ํ์ง ์๋๋ก ์ค๊ณํ๋ ๊ฒ์ ๋งํ๊ณ , OS ์ง์๊ณผ ์์ด์ ํ์ด ์ค๋ช ํ๋ฉด, ์ฐ๋ ๋๊ฐ ๊ฒฝ์ ์ํ(race condition)์ผ ๋, ์๊ณ ๊ตฌ์ญ์ ์๋ ๊ณต์ ๋ณ์์ ์ผ๊ด์ฑ์ ์ ์ง์ํค๋๋ก ์ค๊ณํ๋ ๊ฒ์ ๋งํ๋ค.
1. java.util.concurrent ํจํค์ง ํ์์ ํด๋์ค ์ฌ์ฉ ํ๊ธฐ (Ex. ConcurrentHashMap ๋ฑ)
2. ์ธ์คํด์ค ๋ณ์๋ฅผ ๋์ง ์๊ธฐ = ์ํ ๊ฐ ๋์ง ์๊ธฐ
3. Thread-safeํ singleton ํจํด ์ฌ์ฉํ๊ธฐ (Ex. LazyHolder ๋ฑ)
4. ๋๊ธฐํ ๋ธ๋ญ(synchronized) ์ง์ ํ๊ธฐํนํ 3๋ฒ์ ํด๋นํ๋ ์ด์ผ๊ธฐ๋ ํ ์ด์ผ๊ธฐ๊ฐ ๋ง๊ณ , ์๋ก์ด ๊ฐ๋ ๋ค์ด ๋์ค๋๋ฐ, ์ด ๋ถ๋ถ์ ๋ค์ ๊ธ์์ ์์ธํ ์ค๋ช ํ๋๋ก ํ๊ฒ ๋ค :)
๐Reference
- ์๋ฐ์ ์
- https://techvu.dev/87
- https://cjw-awdsd.tistory.com/42'๊ฐ๋ฐ > Java' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Thread] Runnable and Callable in Java (0) 2022.04.25 [Thread] Volatile์ด๋, Volatile๊ณผ DCL(Double Checking Locking) ๊ณต๋ถ ๊ธฐ๋ก (0) 2022.04.24 [Thread] Program, Process, Thread, Multi-Thread๋? - ๊ตฌ์ฒด์ ์ธ ์๋ ๋ฐฉ์์ ๋ํ ๊ณต๋ถ ๊ธฐ๋ก (0) 2022.04.23 Set, Queue, Map (0) 2022.04.19 [Collection] ArrayList ๊ด๋ จ ๊ผฌ๋ฆฌ ์ง๋ฌธ ๋ต๋ณ ๊ณต๋ถ ๊ธฐ๋ก (0) 2022.04.18