- ๋ชฉ์ฐจ
- ๋ค์ด๊ฐ๋ฉฐ
- ThreadPool์ด ๋ฑ์ฅํ๊ฒ ๋ ๋ฐฐ๊ฒฝ
- ThreadPool์ ์ด๋ป๊ฒ ๋์ํ๋๊ฐ?
- Thread Pool In Java
- ExecutorService
- ๋ง์น๋ฉฐ
- ์ฐธ๊ณ
์ด์ ๊ธ์์ Thread์ ๋ํ ๊ธฐ๋ณธ ๊ฐ๋ ๊ณผ Thread In Java์ ๋ํด์ ๋ค๋ค๋ค๋ฉด ์ด๋ฒ ๊ธ์์ ThreadPool์ ๋ํ ๊ธฐ๋ณธ ๊ฐ๋ ๊ณผ ์๋ฐ์์ ์ด๋ป๊ฒ ์ฌ์ฉ๋๋์ง ๋ค๋ฃฌ๋ค.
์ฐ์ ThreadPool์ ๋ํ ๊ธฐ๋ณธ์ ์ธ ๊ฐ๋
์ ๋ํด์ ๋ค๋ฃจ๊ณ , Java์์ ThreadPool์ ๋ค๋ฃจ๊ธฐ ์ํด ๊ผญ ์์์ผํ๋ Executor์ ExecutorService์ ๋ํด์ ์ ๋ฆฌํ์๋ค.
โ๏ธ ThreadPool์ ์ฌ์ฉํ์ง ์๋๋ค๋ฉด
๋ง์ ํ๋ก๊ทธ๋จ์ ์ค๋ ๋๋ฅผ ์ด์ฉํ์ฌ ๋ณ๋ ฌ (ํน์ ๋ณํ)์ฒ๋ฆฌ๋ฅผ ํ๋ค.
๋ฌธ์ ๋ ์ ์ฐจ ํ์ํ ์ค๋ ๋์ ๊ฐ์๊ฐ ๋ง์์ง๋ฉด์, ์ฆ์ ์ค๋ ๋ ์์ฑ๊ณผ ์ค์ผ์ค๋ง์ผ๋ก ์ธํด CPU์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ์ฆ๊ฐํ๋ค.
๊ฐ ์ค๋ ๋๋ ๋ฉ๋ชจ๋ฆฌ(RAM)๊ณผ ๊ฐ์ ํน์ ์์ค์ ์ปดํจํฐ ๋ฆฌ์์ค๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋์์ ๋๋ฌด ๋ง์ ์ค๋ ๋๊ฐ ํ์ฑ๋๋ค๋ฉด ์ปดํจํฐ์ ์๋๊ฐ ์ ํ๋๋ค.
๋ํ ์ค๋ ๋์ ์์ฑ ๊ฐ์๋ฅผ ์ ์ดํ๊ธฐ ํ๋ค๊ธฐ ๋๋ฌธ์ ์์นซํ๋ฉด ์ปดํจํฐ๊ฐ ๋ฉ์ถ ์๋ ์๋ค.
์๋ฅผ ๋ค์ด, ๋ฉ๋ชจ๋ฆฌ(RAM)์ด ๋๋ฌด ๋ง์ด ์๋ชจ๋์ด OS๊ฐ RAM์ Disk๋ก ์ค์์์ํ๊ธฐ ์์ํ๋ฉด ์๋๋ ํ์ ํ ๋๋ ค์ง๋ค.
๋ฌผ๋ก ์ค๋ ๋ ์์ฑ๋ฟ๋ง ์๋๋ผ ํ์๋ ํด์ผํ๋ค..
โ๏ธ ์๋ฐ ์ค๋ ๋์ ๋ฌธ์
์๋ฐ ์ค๋ ๋๋ ์ง์ ์ด์์ฒด์ ์ค๋ ๋์ ์ ๊ทผํ๋ค. ์ด๋ ์ค๋ ๋๋ฅผ ์์ฑํ๊ณ ํ์ํ๋ ๋น์ฉ์ ์๊ฐ๋ณด๋ค ํฌ๋ฉฐ, ๋์ฑ์ด ์ด์์ฒด์ ์ค๋ ๋์ ์ซ์๋ ์ ํ๋์ด์๋ค.
๋ง์ฝ ์ด์์ฒด์ ๊ฐ ์ ๊ณตํ๋ ์ค๋ ๋ ๊ฐ์์ด์์ ์ค๋ ๋๋ฅผ ์์ฑํ์ฌ ์ฌ์ฉํ๋ค๋ฉด ์๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์์์น ๋ชปํ๊ฒ ํฌ๋์๋ ์ ์๋ค.
๐ ThreadPool์ ์ฌ์ฉํ๋ค๋ฉด
ThreadPool์ ํต์ฌ์ ์ค๋ ๋ ์ฌ์ฌ์ฉ์ด๋ค.
ThreadPool์ ์ฌ์ฉํ์ง ์๋ ๊ธฐ์กด์ Thread ์ ๋ต์ ํ๋ฒ ์ฌ์ฉํ๋ฉด ์ข ๋ฃ๋๊ณ ๋ค์ ๋ง๋ค์ด์ ํ๋ค.
ํ์ง๋ง ThreadPool์ ์ ํด์ง ์ซ์๋งํผ ๋ง๋ค์ด๋ ๊ธฐ์กด ์ค๋ ๋๋ฅผ ์ข ๋ฃ์ํค์ง์๊ณ ๊ณ์ํด์ ์์ ์ ๋งก๊ฒจ ์คํํ๊ฒ ๋๋ค.
์ค๋ ๋๋ฅผ ๋ฏธ๋ฆฌ ๋ง๋ค์ด๋๊ณ ์์ ์ด ๋ค์ด์ฌ๋ฉด ์ค๋ ๋๋ค์๊ฒ ์์ ์ ์ ์ ํ ๋ถ๋ฐฐํ๋ค.
๐ค ๊ทธ๋์ ๋ญ๊ฐ ์ข๋ค๋๊ฑด๋ฐ? - Thread Pool์ ์ฅ์
๊ฒฐ๋ก ์ ์ผ๋ก ์ ํด์ง ์ซ์์ ๊ธฐ์กด ์ค๋ ๋๋ฅผ ์ฌ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋ฆฌ์์ค ์ฌ์ฉ๋์ ์ ์ฝํ๊ณ , ์ด ์ฒ๋ฆฌ๋๋ ๋ ๋์ผ ์ ์๋ค.
- ์ค๋ ๋ ํ์ ์ฌ์ฉํ๋ฉด ํ ๋ฒ์ ํ์ฑ ์ํ์ธ ์ค๋ ๋ ์๋ฅผ ์ฝ๊ฒ ์ ์ดํ ์ ์๋ค. ์ด๋ฅผ ํตํด ์ค๋ ๋์ ์์ฑ๊ณผ ํ์๋ก ์ธํ ๋ถํ๋ฅผ ์ค์ผ ์ ์๋ค.
- ํ ๋ฒ์ ๋ง์ ์์ฒญ์ด ๋ค์ด์ฌ ๋ ๋น ๋ฅด๊ณ ํจ์จ์ ์ผ๋ก ๋์ ์์ ์ ์ํํ ์ ์๋ค.
โ๏ธ ๊ทธ๋ ๋ค๊ณ ์ข์ ๊ฒ๋ง์ ์๋๋ค - Thread Pool์ ๋จ์
- ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น
- ThreadPool์ Thread๋ฅผ ๋ฏธ๋ฆฌ ๋ง๋ค์ด๋๊ณ ์ฌ์ฌ์ฉํ๋ค๋ ์ธก๋ฉด์์ ๊ต์ฅํ ์ ์ฉํ ์๋ฃจ์ ์ด๋ค.
- ํ์ง๋ง ๋ช ๊ฐ์ Thread๋ฅผ ๋ฏธ๋ฆฌ ๋ง๋ค์ด๋์ง๊ฐ ์ค์ํ๋ค. ๋ง์ฝ ๋๋ฌด ๋ง์ ์ค๋ ๋๋ฅผ ๋ง๋ค์ด๋๋ค๋ฉด ๋ฉ๋ชจ๋ฆฌ๋ง ์ฐจ์งํ๊ณ ์๋ฌด๊ฒ๋ ํ์ง ์๋ ์ค๋ ๋๊ฐ ์กด์ฌํ ์๋ ์๋ค.
- ๋๊ณ ๋จน๋ ์ค๋ ๋ ๋ฐ์ ๊ฐ๋ฅ์ฑ
- ๋ณ๋ ฌ์ ์ผ๋ก ์ฒ๋ฆฌ์์ฒญ์ ํ๋ค๋ฉด ํ ์ค๋ ๋๋ ์์ฒญ์ ์ฒ๋ฆฌํ๋๋ผ ํ๋์ด๊ณ ์๋์์ค์ ๋ค๋ฅธ ์ค๋ ๋๋ ๋๊ณ ์์ ์ ์๋ค.
- ์ด๋ก์ธํด ๋ฏธ๋ฆฌ ๋ง๋ค์ด๋ ์ค๋ ๋๋ฅผ ์ ํ์ฉํ์ง ๋ชปํ ์๋ ์๋ ๋ฌธ์ ๊ฐ ์๋ค.
- ๋ฌผ๋ก ์ด๋ ์๋ฐ์์๋
ForkJoinPool์ ์ง์ํ๋ค.
- ๋๊ณ ๋จน๊ฑฐ๋ I/O ํน์ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ ๊ธฐ๋ค๋ฆฌ๋ ์ค๋ ๋ ์ฌ์ฉ์ ์ฃผ์ํด์ผํ๋ค.
- ์ค๋ ๋๊ฐ ์ ์ ์๊ฑฐ๋ I/O ํน์ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ ๊ธฐ๋ค๋ฆฌ๋ฉด ํด๋น Task๋ฅผ ์คํํ๋ ์ค๋ ๋๋ Blocking๋ ํ๋ฅ ์ด ๋๋ค. ์ฆ, ์ค๋ ๋๋ฅผ ํ ๋น๋ฐ์์ผ๋ฉด์ ์๋ฌด ์์ ๋ ํ์ง์๊ณ ์๋ ๊ฒ์ด๋ค.
- ๊ทธ๋ฌ๋ฏ๋ก ๊ฐ๋ฅํ Blocking (์๊ฑฐ๋ ์ด๋ฒคํธ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋)์ ๋ฐ์์ํค๋ Task๋ ์ค๋ ๋ ํ์ ์ด์ฉํ์ง์๋๊ฒ ์ข๋ค. (๋ฌผ๋ก ์ด๋ ์งํค๊ธฐ์ด๋ ต๋ค...)
- DeadLock
- ThreadPool๋ง์ ๋ฌธ์ ๋ผ๊ธฐ๋ณด๋ค๋ ๋ฉํฐ์ค๋ ๋ฉ ํ๊ฒฝ์์ ๋ฐ์ํ ์ ์๋ ๋ํ์ ์ธ ๋ฌธ์ ์ ์ ๋ฐ๋๋ฝ์ด๋ค. ThreadPool๋ ๋ฐ๋๋ฝ์ด ๋ฐ์ํ ์ ์๋ค.
- ThreadPool์ ๊ฒฝ์ฐ, ๋ง์ฝ ํ๋์ Task์ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ๋ ๋ค๋ฅธ Task๋ฅผ Queue์ ๋ฃ์ผ๋ฉด ๋ฐ๋๋ฝ์ด ๋ฐ์ํ ํ๋ฅ ์ด ๋๋ค.
- ์ค๋ ๋ ์์ด์ง ๋ฌธ์
- ThreadPool์ ๋ฏธ๋ฆฌ ์์ฑ๋ ์ค๋ ๋๊ฐ ์์ฒญ์ ์ฒ๋ฆฌํ๊ณ ๋์ ๋ค์ ๋๊ธฐ์ํ๊ฐ ์๋๊ณ ์ ๊ฑฐ๋ ์ ์๋ค. (์ฌ์ฌ์ฉ ๋ถ๊ฐ)
- ์๋ฅผ ๋ค์ด, ์ค๋ ๋์ ํ ๋น๋ Task๊ฐ ์์ธ๋ฅผ ๋์ก๋๋ฐ ํด๋น ์์ธ๋ฅผ ์ก์ง ๋ชปํ๋ค๋ฉด Thread ์์ฒด๊ฐ ์ข ๋ฃ๋๊ธฐ ๋๋ฌธ์ ThreadPool์์ ์ค๋ ๋์ ๊ฐ์๊ฐ ํ๋์ฉ ์ค์ด๋ค ์ ์๋ค.

์ถ์ฒ: https://www.logicbig.com/tutorials/core-java-tutorial/java-multi-threading/thread-pools.html
์ฌ์ค ์ ๊ทธ๋ฆผ๋ง๋ด๋ ThreadPool์ด ์ด๋ป๊ฒ ๋์ํ๋์ง ์ ์ ์๋ค. ๊ทธ๋๋ ์กฐ๊ธ ๊ธ๋ก ์ ๋ฆฌํ์๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
- ThreadPool์ ์๊ตฌ์ ๋ง๋ ์ค๋ ๋๋ฅผ ์ฌ์ฌ์ฉํ๊ธฐ ์ํด ๋ฏธ๋ฆฌ ์์ฑํด๋๋ค. (New ThreadPool)
- ์๋ก์ด ์์ ์ด ๋ค์ด์ฌ ๋๋ง๋ค TaskQueue์ ๋ฃ๋๋ค. (New Task)
- TaskQueue์์ ํ๋์ฉ ๊บผ๋ด์ ๋ถํ ๋ฐฉ์์๋ฐ๋ผ ์ ํด ์ค๋ ๋์ ์์ ์ ํ ๋นํ๋ค. (Thread Assignment)
- ๊ฐ ์์ ์ด ์๋ฃ๋๋ฉด, ์ฝ๋ฐฑ ํํ๋ก ์์ ์ ์์ฒญํ ์ฃผ์ฒด์๊ฒ ๊ฒฐ๊ณผ๋ฅผ ์๋ ค์ค๋ค. (Task Finished)
ThreadPool์ ์ด๋ ต๊ณ ๋ณต์กํ ๊ธฐ์ ์ด๋ผ๊ธฐ๋ณด๋ค๋ ๊ทธ์ Thread๋ฅผ ์ ํ์ฉํ ์ ์๊ฒํด์ฃผ๋ ๋์์ธํจํด์ ๊ฐ๊น๋ค๊ณ ์๊ฐํ๋ค.
์ด์ ์๋ฐ์์๋ ์ด๋ป๊ฒ Thread Pool์ ์ฌ์ฉํ ์ ์๋์ง ์ดํด๋ณธ๋ค.
์๋ฐ๋ 5๋ฒ์ ๋ถํฐ ์์ฒด์ ์ผ๋ก Thread Pool์ ์ง์ํ๊ธฐ ์์ํ๋ค. java.util.concurrent ํจํค์ง์ ThreadPool๊ณผ ๊ด๋ จ๋ ํด๋์ค๊ฐ ์กด์ฌํ๋ค.
์๋ฐ์์์ ThreadPool๋ ์๋์ ๊ฐ์ด ๋์ผํ๊ฒ ๋์ํ๋ค.

์ถ์ฒ: https://www.baeldung.com/thread-pool-java-and-guava
์ค๋ ๋ ํ์ ์ค์ ๊ตฌํ์์ ์ฝ๋๋ฅผ ๋ถ๋ฆฌํ ์ํ๋ก ์ ์งํ๊ณ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฒด์์ ์ด๋ฌํ ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
์๋ฐ๋ ThreadPool๊ณผ ๊ด๋ จ๋ ์ฝ๋๋ฅผ ๋น์ฆ๋์ค ๋ก์ง๊ณผ ๋ถ๋ฆฌ์ํค๊ธฐ์ํด Executor๊ณผ ExecutorService๋ผ๋ ์ธํฐํ์ด์ค๋ก ์ถ์ํ์์ผฐ์ผ๋ฉฐ, Executors๋ผ๋ ์ ํธ์ฑ ํด๋์ค๋ฅผ ๋ง๋ค์ด ํธ์ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค.
์๋ฐ๋ ์์ฝ๊ฒ ์ค๋ ๋ ํ์ ์ ์ดํ ์ ์๋๋ก Executor์ ExecutorService๋ผ๋ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ค.
์ค์ ๋ก ์๋์ ๊ฐ์ด ์์ฝ๊ฒ ์ค๋ ๋ ํ์ ์์ฑํ ์ ์๋ค.
ThreadPool ์์ฑ ์์ (์ค๋ ๋ 100๊ฐ)
ExecutorService threadPool = Executors.newFixedThreadPool(100);์์ฑํ ์ค๋ ๋ ํ์ Task (Runnable ํน์ Callable)์ ์ ๊ณตํ๋ฉด ์์ฝ๊ฒ ์ค๋ ๋ ํ์์ ์ค๋ ๋๋ก ์ด๋ฅผ ์คํ์ํฌ ์ ์๊ฒ๋๋ค.
์๋ฐ์์๋ ThreadPool๊ณผ ๊ด๋ จ๋ ์ฝ๋๋ฅผ Executor๊ณผ ExecutorService๋ก ์ถ์ํ์์ผฐ๋ค. ๊ทธ๋ ๋ค๋ฉด ์ด ๋์ ์ฐจ์ด์ ์ ๋ฌด์์ผ๊น?
๐ค Executor
public interface Executor {
// Executes the given command at some time in the future.
// The command may execute in a new thread, in a pooled thread, or in the calling thread, at the discretion of the Executor implementation.
void execute(Runnable command);
}Executor๋ ๋๊ฒจ๋ฐ์ Runnable Task๋ฅผ ์คํํ๋ ๋ฉ์๋๋ฅผ ๊ฐ์ง ๊ฐ๋จํ ์ธํฐํ์ด์ค๋ค.
์ธํฐํ์ด์ค๋ก ๋ฐ๋ก ๋นผ๋ ์ด์ ๋ ๋๊ฒจ๋ฐ์ Task๋ฅผ ์ค๋ ๋๊ฐ ์ด๋ป๊ฒ ์คํํ ์ง, ์ค๋ ๋ ์ค์ผ์ค๋ง์ ์ด๋ป๊ฒ ํ ์ง๋ฑ์ ์ถ์ํ์ํค๊ธฐ ์ํจ์ด๋ค.
์ฝ๊ฒ ์๊ธฐํ์๋ฉด ๊ธฐ์กด์ new Thread(new RunnableTask()).start()ํด์ผํ๋ ์์
์ ์๋์ ๊ฐ์ด ์ถ์ํ ์ํจ๊ฒ์ด๋ค.
Executor executor = anExecutor;
executor.execute(new RunnableTask1()); // ์๋ก์ด Task๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ง๋ Executor ๊ตฌํ์ฒด์ ๋ฐ๋ผ ๋ค๋ฅด๋ค.
executor.execute(new RunnableTask2());์๋ฐ๋ ์ค๋ ๋๋ฅผ ์ง์ ๊ตฌํํ์ฌ ์ฌ์ฉํ ์ ์๋๋ก ์ง์ํ๋ค.
ํ์ง๋ง.. ์ค๋ ๋๋ฅผ ์ง์ ๊ตฌํํ์ฌ ์ฌ์ฉํ๋ฉด, ์ฝ๋ ๊ฐ๋ ์ฑ์ด ๋๋ฌด ๋จ์ด์ง๋ฉฐ, ์ฝ๋๋ง ๋ณด๊ณ ๋ ์ ํํ ์ด๋ป๊ฒ ๋์ํ๋์ง ์๊ธฐ ์ฝ์ง์๋ค.
๊ทธ๋ฆฌํ์ฌ Thread Pool์ ๊ฐ๋ ๊ณผ ๊ฐ์ด ์๋ฐ๋ Thread๋ฅผ ๊ด๋ฆฌํ๊ณ ๋ถ๊ฐ ๋ก์ง๊ณผ ๋น์ฆ๋์ค ๋ก์ง์ธ ํต์ฌ ๋ก์ง์ ๋ถ๋ฆฌ์ํค๊ธฐ์ํด ๊ณ ์์ค์ ์ธํฐํ์ด์ค์ธ
Executor,ExecutorService,Executors๋ฑ์ ๋ง๋ค์๋ค.์ฆ, Thread๋ฅผ ์์ฑํ๊ณ ๊ด๋ฆฌํ๋ ๋ถ๊ฐ๋ก์ง์ ํต์ฌ ๋ก์ง์ผ๋ก๋ถํฐ ๋ถ๋ฆฌ์ํจ ๊ฒ์ด๋ค.
์ด๋ ๊ฒ ์ถ์ํ์ํด์ผ๋ก์จ ์๋์๊ฐ์ด ์ํฉ์ ๋ง๊ฒ Executor ๊ตฌํ์ฒด๋ฅผ ๋ง๋ค์ด Task๋ฅผ ์ฒ๋ฆฌํ ์ ์๊ฒ ๋์๋ค.
- ๋๊ธฐ์ ์ผ๋ก Task ์ฒ๋ฆฌ (Thread๋ฅผ ํ์ฉํ ๋น๋๊ธฐ๋ฅผ ์ฌ์ฉํ์ง ์์)
class DirectExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
}- ๋งค Task๋ง๋ค ์๋ก์ด ์ค๋ ๋๋ฅผ ์์ฑํ์ฌ ์ฒ๋ฆฌ
class ThreadPerTaskExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}
}- Task๋ฅผ ํน์ ์ค๋ ๋์ ์ค์ผ์ค๋งํ ๋ ์ปดํฌ์งํธ ํน์ ํ๋ก์ ํจํด๊ณผ ๊ฐ์ด ์ฌ์ฉํ์ฌ ์ฒ๋ฆฌํ ์ ์๋ค.
class SerialExecutor implements Executor {
final Queue<Runnable> tasks = new ArrayDeque<Runnable>();
final Executor executor;
Runnable active;
SerialExecutor(Executor executor) {
this.executor = executor;
}
public synchronized void execute(final Runnable r) {
tasks.offer(new Runnable() {
try {
r.run();
} final {
// ๋งค Task๋ง๋ค ๋๋๋ฉด ๋ค์ Task ํธ์ถ.
scheduleNext();
}
});
// ๋ง์ฝ ํ์ฌ active(์คํ)๋๊ณ ์๋ Task๊ฐ ์๋ค๋ฉด ๋ค์ Task ํธ์ถ.
if (active == null) {
scheduleNext();
}
}
// TaskQueue์์ ๋ค์ Task๋ฅผ ๊บผ๋ด์ ์ค๋ ๋์ ์ค์ผ์ค๋ง. (์คํ)
protected synchronized void scheduleNext() {
if ((active == tasks.poll() != null)) {
executor.execute(active);
}
}
}์์ ๊ฐ์ด Executor๋ผ๋ ์ธํฐํ์ด์ค๋ฅผ ๋ถ๋ฆฌ์ํด์ผ๋ก์จ ์ํฉ์ ๋ง๋ ๊ตฌํ์ฒด๋ฅผ ๋ง๋ค๊ฑฐ๋ ์กฐํฉํด์ ๋ค์ํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
๐ค ExecutorService
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}ExecutorService๋ Task๋ฅผ ๊ด๋ฆฌํ๊ณ ์ ์ดํ๋๋ฐ ์ฌ์ฉ๋๋ Execute ์ธํฐํ์ด์ค์ ํ์ ์ธํฐํ์ด์ค์ด๋ค.
์ฝ๊ฒ ๋งํด Execute๋ณด๋ค ๋ ํ์ฅ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๊ธฐ์ํ ์ธํฐํ์ด์ค์ด๋ค.
ThreadPool์ ์ข
๋ฃํ๋ ๋ฉ์๋์ ํ๋ ์ด์์ ๋น๋๊ธฐ ์์
์ ์ถ์ ํ๊ธฐ ์ํ Future๋ฅผ ๋ฐํํ๋ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค.
ExecutorService๋ ์์ ํ๊ฒ ์ข ๋ฃํ ์ ์๊ฒ ๋ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค.shutdown(): ์ข ๋ฃ์ ์ ์์ฒญ๋ Task (์คํ์ค์ธ Task + ๋๊ธฐ์ค์ธ Task)๋ ๋ชจ๋ ์ํํ๋๋กํ๊ณ ์ข ๋ฃ๋๋ค.shutdownNow(): ๋๊ธฐ ์ค์ธ Task๊ฐ ์์๋๋ ๊ฒ์ ๋ฉ์ถ๋๋กํ๊ณ , ํ์ฌ ์คํ์ค์ธ Task๋ ์ค์งํ๋ค.
ExecutorService๋Executor.execute(Runnable)๋ฅผ ํ์ฅํ์ฌ Task๋ฅผ ์ทจ์ํ๊ฑฐ๋ ์๋ฃ๋ฅผ ๊ธฐ๋ค๋ฆด ๋ ์ฌ์ฉ๋๋Future์ ๋ง๋ค์ด ๋ฐํํ๋ค.ExecutorService๋invokeAny์invokeAll์ ํตํด ์ฌ๋ฌ ๊ฐ์ Task๋ฅผ ํ๋ฒ์ Submit๋๋๋ก ๋ฒํฌ ์ฐ์ฐ์ ์ง์ํ๋ค.
๐ค Executor vs ExecutorService
Executor: ์ฃผ์ด์ง Runnable์ ์คํ๋ง ํ๋ค.ExecutorService:Execute์ ํ์ฅ ->Execute์ ์คํ๋ฟ๋ง ์๋๋ผ ์์ํ๊ฑฐ๋ ์ข ๋ฃํ๋๋ฑ์ ์ฌ๋ฌ ๊ธฐ๋ฅ์ ํ์ฅ.
๐โโ๏ธ ๊ฐ๋จ ์์ - Thread Pool์ ํ์ฉํ์ฌ ๋ค์ด์ค๋ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ์์
NetworkService.java
class NetworkService implements Runnable {
private final ServerSocket serverSocket;
private final ExecutorService pool;
public NetworkService(int port, int poolSize) throws IOException {
serverSocket = new ServerSocket(port);
pool = Executors.newFixedThreadPool(poolSize);
}
public void run {
try {
for (;;) {
pool.execute(new Handler(serverSocket.accept()));
}
} catch (IOException ex) {
pool.shutdown();
}
}
}Handler.java
class Handler implements Runnable {
private final Socket socket;
Handler (Socket socket) {
this.socket = socket;
}
public void run() {
// read and service request on socket
}
}์๋ฐ์์ ์ ๊ณตํ๋ FixedThreadPool์ ์ด์ฉํ ๊ฐ๋จํ ์ค๋ ๋ ํ ์์
Task.java
public class Task implements Runnable {
private String name;
public Task(String name) {
this.name = name;
}
@Override
public void run() {
try {
for (int i = 0; i < 5; i++) {
Date now = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm:ss");
if (i == 0) {
System.out.println(this.name + " ์์ - " + dateFormat.format(now));
} else {
System.out.println(this.name + " ์คํ - " + dateFormat.format(now));
}
Thread.sleep(1_000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.name + " Task ์๋ฃ");
}
}ExampleMain.java
public class ExampleMain {
public static void main(String[] args) {
// given
Runnable task1 = new Task("Task 1");
Runnable task2 = new Task("Task 2");
Runnable task3 = new Task("Task 3");
Runnable task4 = new Task("Task 4");
Runnable task5 = new Task("Task 5");
ExecutorService threadPool = Executors.newFixedThreadPool(3); // ThreadPool์ Queue ์ต๋ ์ฌ์ด์ฆ 3๊ฐ ์ค์
// when
threadPool.execute(task1);
threadPool.execute(task2);
threadPool.execute(task3);
threadPool.execute(task4);
threadPool.execute(task5);
// and
threadPool.shutdown();
}
}- ThreadPool์ ThreadCore ์ต๋ ์ฌ์ด์ฆ๋ฅผ 3๊ฐ๋ก ์ค์ ํ์ฌ 5๊ฐ์ Task๋ฅผ ์์ฒญํ ์์์ด๋ค.
- ์ต๋ ์ฌ์ด์ฆ๊ฐ 3์ด๊ธฐ ๋๋ฌธ์
Task4์Task5๋Task1, 2, 3์ค ํ๋๊ฐ ๋๋์๋ฃ์ํ๊ฐ๋์์ผ ์์ํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
- ์ต๋ ์ฌ์ด์ฆ๊ฐ 3์ด๊ธฐ ๋๋ฌธ์
์๋ฐ์์ ThreadPool์ ์ฌ์ฉํ๋ ์ฝ๋๋ฅผ ๋ณด๋ฉด ๋๋ถ๋ถ ExecutorService๋ฅผ ๊ตฌํํ ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ค. ๊ฐ์ฅ ์ค์ํ๋ค๊ณ ๋ณผ ์ ์๋ค.
์ค์ ๋ก HikariCP์ ์ผ๋ถ ์ฝ๋๋ฅผ ๋ณด๋ฉด
ExecutorService๋ฅผ ๊ตฌํํThreadPoolExecutor๋ฅผ ์ฌ์ฉํ๋ค.์ ํํ ๊ฒ์ ์๋๋ค.. ์ถํ์ HikariCP๋ฅผ ์ ๋ฆฌํ ๋ ํ๋ฒ ๋ ์์๋ณผ ์์ .
์ด๋ฒ ์ฑํฐ์์ ExecutorService์ ๋ํด์ ์ ๋ฆฌํ์๋ค.
์ฐ์ ์ด๋ป๊ฒ ์์ฑํ๋์ง, ์ด๋ค ๊ตฌํ์ฒด๊ฐ ์๋์ง ์ค๋ช ํ๋ค. ๊ทธ๋ฆฌ๊ณ Task๋ฅผ ์ด๋ค ๋ฐฉ์์ผ๋ก ์์ฒญํ๊ณ ์ปจํธ๋กคํ๋์ง์ ๋ํด์ ๋ค๋ฃฌ๋ค.
ExecutorService๊ฐ ๋ฌด์์ธ์ง์ ๋ํด์ ์์ ์ ๋ฆฌํ ๋ด์ฉ์ ์ฐธ๊ณ .
์๋ฐ์์ ์ ๊ณตํ๋ ExecutorService์ ๊ตฌํ์ฒด๋ ๋ค์ํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ๊ฐ์ฒด๋ ThreadPool ์ญํ ์ ํ๋ค.
๊ทธ๋ฐ ์๋ฏธ์์ ExecutorService๋ฅผ ๊ตฌํํ ๊ตฌํ์ฒด ๋๋ถ๋ถ์ ThreadPool์ด๋ผ๊ณ ๋ด๋ ๋ ๊ฒ ๊ฐ๋ค.
ExecutorService์ ๊ตฌํ์ฒด๋ ํฌ๊ฒ ๋ ๊ฐ๋ก ๋๋๋ค.
java.util.concurrent.ThreadPoolExecutorjava.util.concurrent.ScheduledExecutorService
๐โโ๏ธ java.util.concurrent.ThreadPoolExecutor
// ์ง์ ์์ฑ
int corePoolSize = 5;
int maxPoolSize = 10;
long keepAliveTime = 5000;
ExecutorService threadPoolExecutor =
new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()
);
// Executors ์ด์ฉํ์ฌ ์์ฑ
ExecutorService executorService = Executors.newSingleThreadExecutor();
ExecutorService executorService = Executors.newFixedThreadPool(int nThreads);
ExecutorService executorService = Executors.newCachedThreadPool();- ๋์ ๊ณผ์
- ์ฃผ์ด์ง Task (Callable ํน์ Runnable)์ ๋ด๋ถ์ ์ผ๋ก ๋์ํ๊ณ ์๋ ThreadPool์ ์ด์ฉํด์ ์คํํ๋ค.
- ์ค์
- Thread ๊ฐ์
corePoolSize: Thread ๊ฐ์ (Thread๊ฐ ๋๊ณ ์์ด๋ ๊ณ์ ์ ์ง๋๋ ๊ฐ์)maximumPoolSize: ์ต๋ Thread ๊ฐ์ (TaskQueue๊ฐ Full์ธ ์ํ์์ corePoolSize๋ณด๋ค ๋ ๋ง์ ์ค๋ ๋๊ฐ ํ์ํ๋ฉด Thread๋ฅผ ์์ฑํ๋ค. ์ด๋์ ์ต๋ ์์ฑ ๊ฐ์)
KeepAliveTime: Thread KeepAlive ์๊ฐunit: KeepAliveTime ๋จ์workQueue: TaskQueue์ ๊ตฌํ์ฒด (ex. Runnable์ ์ ์ฅํ๋ BlockingQueue)
- Thread ๊ฐ์
- Executors์์ ์ ๊ณตํ๋ ํธ์ ๋ฉ์๋
Executors.newSingleThreadExecutor(): ๋จ์ผ ์ค๋ ๋ ์์ฑ- ์คํจ ์ ์๋ก ์ค๋ ๋๋ฅผ ์์ฑํ์ง ์๋๋ค ->
corePoolSize: 1, maximumPoolSize: 1
- ์คํจ ์ ์๋ก ์ค๋ ๋๋ฅผ ์์ฑํ์ง ์๋๋ค ->
Executors.newFixedThreadPool(int nThreads): ๊ณ ์ ๊ฐ์์ ์ค๋ ๋ ์์ฑ.- ๋ชจ๋ ์ค๋ ๋๊ฐ ์์
์ค์ด๋ฉด TaskQueue์ ์์
์ ์ ์ฌํ๋ค. ->
corePoolSize: n, maximumPoolSize: n
- ๋ชจ๋ ์ค๋ ๋๊ฐ ์์
์ค์ด๋ฉด TaskQueue์ ์์
์ ์ ์ฌํ๋ค. ->
Executors.newCachedThreadPool(): ํ์์ ๋ฐ๋ผ ์๋ก์ด ์ค๋ ๋๋ฅผ ์์ฑํ๋ฉฐ, ์ด์ ์ ์์ฑํ๋ ์ค๋ ๋๊ฐ ์กด์ฌํ๋ฉด ์ฌ์ฌ์ฉํ๋ค.- ๋ํดํธ๋ก๋ 60์ด๋์ ์ค๋ ๋๊ฐ ์ ์ง๋๋ค. ->
corePoolSize: 0, maximumPoolSize: Integer.MAX_VALUE
- ๋ํดํธ๋ก๋ 60์ด๋์ ์ค๋ ๋๊ฐ ์ ์ง๋๋ค. ->
์ค์ ์ ๋ํ ๋ ์์ธํ ๋ด์ฉ์ ์ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ .
๐โโ๏ธ java.util.concurrent.ScheduledThreadPoolExecutor
public class ScheduledExecutorExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ScheduledExecutorService scheduledExecutorService =
Executors.newScheduledThreadPool(5);
ScheduledFuture<String> scheduledFuture =
scheduledExecutorService.schedule(
() -> {
System.out.println("Executed!");
return "Callable Result";
},
5,
TimeUnit.SECONDS
);
System.out.println("์คํ ๊ฒฐ๊ณผ (5์ดํ) : " + scheduledFuture.get());
scheduledExecutorService.shutdown();
}
}
// ๊ฒฐ๊ณผ
Executed!
์คํ ๊ฒฐ๊ณผ (5์ดํ) : Callable Result- ๋์๊ณผ์
- ์ง์ ๋ ์๊ฐ๋งํผ Delayํ Task๋ฅผ ์คํํ๊ฑฐ๋ ์ฃผ๊ธฐ์ ์ผ๋ก ์คํํ๋ค.
ThreadPoolExecutor๊ณผ ๋์ผํ๋ค. ๋ค๋งScheduledExecutorService์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํจ์ผ๋ก์จschedule๋ฉ์๋๋ฅผ ๊ตฌํํ๊ณ ์๋ค.public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {}
- ์ฃผ์ ๋ฉ์๋
schedule(Callable task, long delay, TimeUnit timeunit)schedule(Runnable task, long delay, TimeUnit timeunit)scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit timeunit)scheduleWithFixedDelay(Runnable task, long initialDelay, long period, TimeUnit timeunit)
๋ ์์ธํ ๋ด์ฉ์ ์ฌ๊ธฐ๋ฅผ ํ์ธ.
๋ชจ๋ ์์ฒญ์ ์ฒ๋ฆฌํ๊ณ ๋ ์ด์ ์ฌ์ฉํ์ง ์๋๋ค๋ฉด ์ด๋ ํ ExecutorService๋ shutdown()์ ํด์ผํ๋ค.
์๊ทธ๋ผ JVM์ด ๊ณ์ํด์ ThreadPool์ Thread๊ฐ ํ์ฑํ๋ ์ํ์์ Task๋ฅผ ๊ธฐ๋ค๋ฆฌ๊ธฐ ๋๋ฌธ์ ํ๋ก๊ทธ๋จ์ด ์ข ๋ฃ๋์ง ์๋๋ค.
์ข ๋ฃ์ํค๋ ๋ฉ์๋๋ก๋
shutdown(),shutdownNow(),awaitTermination()๋ฑ์ด ์๋ค.
invoke๊ฐ ๋ถ์ ๋ฉ์๋๋ ์ฌ๋ฌ ๊ฐ์ Task๋ฅผ ํ๋ฒ์ ์์ฒญํ ๋ ์ฌ์ฉ๋๋ค.
๐โโ๏ธ invokeAny()
invokeAny()๋ Callable์ ๊ตฌํํ ๊ฐ์ฒด(Task)์ ์ปฌ๋ ์
์ ๋งค๊ฐ๋ณ์๋ก ๋ฐ๋๋ค.
๋งค Callable์ Future๋ฅผ ๋ฐํํ์ง ์๊ณ , ๋๊ฒจ๋ฐ์ ์ปฌ๋ ์
์ค ํ๋์ Callable์ ๋ํ ๊ฒฐ๊ณผ๋ง์ ๋ฐํํ๋ค.
<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws ...
์ฆ, ์์ฒญํ Task (Callable)์ค์์ ํ๋๋ผ๋ ์๋ฃ๋๋ฉด ํด๋น Task์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๊ณ , ๋๋จธ์ง Task๋ ๋ชจ๋ Cancelํ๋ค.
If one of the tasks complete (or throws an exception), the rest of the Callable's are cancelled.
// invokeAny() ์์
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<>();
callables.add(() -> "Task 1 Completed");
callables.add(() -> "Task 2 Completed");
callables.add(() -> "Task 3 Completed");
String result = executorService.invokeAny(callables);
System.out.println(result); // Task 1 or Task 2 or Task 3
executorService.shutdown();
}
// ๊ฒฐ๊ณผ
// Task 1 Completed ํน์ Task 2 Completed ํน์ Task 3 Completed๐โโ๏ธ invokeAll()
invokeAll() ๋ฉ์๋๋ invokeAny()์ ๋ค๋ฅด๊ฒ ๋ชจ๋ Task๊ฐ ์๋ฃ๋์ด์ผ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
ํ๋๋ผ๋ ์๋ฃ๋์ง ์์๋ค๋ฉด holding๋๋ค.
๊ฒฐ๊ณผ๋ List<Future>๋ฅผ ๋ฐํํ๋ฉฐ, ๋๊ฒจ๋ฐ์ Task (Callable)๊ฐ ์ ์ ์ฒ๋ฆฌ๋์๋ ์์ธ๊ฐ ๋ฐ์ํ์ฌ ๋๋ฌ๋ ์๋ฃ๋ ๊ฒ์ผ๋ก ๋ณธ๋ค.
์ฆ, ๋์์ค์ ์ ๋ฌ๋ฐ์ Task๋ค์ด ๋ณ๊ฒฝ๋๊ฑฐ๋ ์๋ชป๋์ด๋ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฅํ์ง ์๋๋ค.
์๋ชป์ฌ์ฉํ๋ฉด ์์ฒญํ
Callable๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋๋ผ Blocking์ด ๋์ด ์์น์๊ฒ ์ฑ๋ฅ์ด ์ ํ๋ ์๋ ์๋ค.
// invokeAll() ์์
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<>();
callables.add(() -> "Task 1 Completed");
callables.add(() -> "Task 2 Completed");
callables.add(() -> "Task 3 Completed");
// ๋ง์ฝ Task๋ค์ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฐ๋ค๋ฉด ์ฌ๊ธฐ์ Block๋์ด ์ฑ๋ฅ์ด ์ ํ๋ ์๋ ์๋ค.
List<Future<String>> futures = executorService.invokeAll(callables);
for (Future<String> future : futures) {
System.out.println(future.get());
}
executorService.shutdown();
}
// ๊ฒฐ๊ณผ
Task 2 Completed
Task 3 Completed
Task 1 CompletedExecutorService์ ์์ฒญํ Task (Runnable ํน์ Callable)์ Future.cancel()๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ์ทจ์ํ ์ ์๋ค.
๋ค๋ง, Task๊ฐ ์์ง ์คํ์ ์ผ ๋๋ง ์ทจ์ํ ์ ์๋ค. ๋ง์ฝ ์คํ์ค์ด๋ผ๋ฉด ์ทจ์ํ ์ ์๋ค.
future.cancel();์์๋ณด๋ค ๊ธ์ด ๊ธธ์ด์ก๋ค. ๊ทธ๋๋ ThreadPool์ ๋ํ ๊ฐ๋ ์ ์ก์ ์ ์์๊ณ , ์๋ฐ์์ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ๋์ง ํฐ๊ทธ๋ฆผ์ ๊ทธ๋ฆด ์ ์์๋ค.
๋๊ตฐ๊ฐ์๊ฒ ๋์์ด ๋๋ ๊ธ์ด ๋๊ธฐ๋ฅผ~~~
๊ฐ๋จํ ThreadPool์ ์ง์ ๊ตฌํํด๋ณด๊ณ ์ถ๋ค๋ฉด ์ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํ๋ฉด ๋๋ค.
ํ์๋ ์ง์ ๊ตฌํํด๋ณด๋ ๋ง์ ๋์์ด ๋์๋ค.
๋ค์ ๊ธ๋ถํฐ ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ๋ํด์ ์ ๋ฆฌํ๋ค. ์๋ง ๋ค์ ๊ธ์ Future์ ๋ํ ๊ธ์ผ ๋ฏ ์ถ๋ค.
- https://en.wikipedia.org/wiki/Thread_pool
- http://tutorials.jenkov.com/java-concurrency/thread-pools.html
- http://tutorials.jenkov.com/java-util-concurrent/executorservice.html
- https://www.javatpoint.com/java-thread-pool
- https://stackoverflow.com/questions/15052317/whats-the-difference-between-executor-and-executorservice
- https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html
- https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
- https://docs.oracle.com/javase/tutorial/essential/concurrency/executors.html
