π§Ά Java μμ μ€λ λ ν(Thread Pool) μ μ¬μ©ν΄ 보μ
νλ‘μΈμ€ λ΄μμ μ€λ λμ μμ± λ° μκ±°κ° λΉλ²νκ² λ°μνλ€λ©΄ λ©λͺ¨λ¦¬ ν λΉμ μλͺ¨λλ λΉμ©μ΄ λ§μ΄ λ€μ§ μμκΉ? μ΄μ λν ν΄λ΅μΌλ‘ μ€λ λ νμ λν΄ μμλ³΄κ³ Java μμ μ΄λ»κ² μ¬μ©νλ©΄ λλμ§ μμ보μ.
μ€λ λ ν(Thread Pool)
μ€λ λ μ μ΄ λ¬Έμ λ₯Ό ν΄κ²°ν λ°©λ²μΌλ‘ μ€λ λ νμ μ¬μ©νλ€. μ€λ λ νμ λ§€λ² μμ± λ° μκ±° μμ²μ΄ μ¬ λ μ€λ λλ₯Ό μμ±νκ³ μκ±°νλ κ²μ΄ μλ, μ€λ λ μ¬μ©μκ° μ€μ ν΄λ κ°μλ§νΌ 미리 μμ±ν΄λλ κ²μ΄λ€.
μ€λ λ(Thread)
- μ΄λ€ νλ‘κ·Έλ¨ λ΄μμ μ€νλλ νλ¦μ λ¨μ
- νΉν νλ‘μΈμ€ λ΄μμ μ€νλλ νλ¦μ λ¨μ
ν(Pool)
- νμν λλ§λ€ κ°μ²΄λ₯Ό ν λΉνκ³ νκ΄΄νλ λμ , μ¬μ© μ€λΉλ μνλ‘ μ΄κΈ°νλ κ°μ²΄ μ§ν©
μ€λ λλ λμΌν λ©λͺ¨λ¦¬ μμμμ μμ± λ° κ΄λ¦¬κ° μ΄λ£¨μ΄μ§μ§λ§, μμ±νκ±°λ μκ±°ν λ 컀λ μ€λΈμ νΈλ₯Ό λλ°νλ 리μμ€μ΄λ―λ‘ μμ± λΉμ©μ΄ ν¬κ² λ°μνλ€. μ€λ λλ₯Ό μ μ΄ν μ μλ μνμμ μ€λ λλ₯Ό 무차λ³μ μΌλ‘ μμ±νλ©΄ 리μμ€κ° λΉ λ₯΄κ² μμ§λλ μν©μ΄ λ°μν μ μλ€. κ·Έλ¬λ©΄ μ΄λ»κ² νλ©΄ ν¨μ¨μ μΌλ‘ μ€λ λλ₯Ό μ μ΄ν μ μμκΉ? μ€λ λ νμ λμ λ°©μμ κ°λ¨νκ² λ³΄λ©΄ λ€μκ³Ό κ°λ€.
- λ³λ ¬ μμ μ ννλ‘ λμ μ½λλ₯Ό μμ±νλ€.
- μ€νμ μν΄ μ€λ λ νμ μΈμ€ν΄μ€μ μ μΆνλ€.
- μ μΆν μΈμ€ν΄μ€μμ μ€ννκΈ° μν΄ μ¬μ¬μ©λλ μ¬λ¬ μ€λ λλ₯Ό μ μ΄νλ€.
μ€λ λ νμ μ¬μ©νλ©΄ λΉμ©μ μΈ μΈ‘λ©΄μ΄λ 컨ν μ€νΈ μ€μμΉκ° λ°μνλ μν©μμ λλ μ΄λ₯Ό μ€μΌ μ μλ€λ μ₯μ μ΄ μλ€. κ·Έλ λ€λ©΄ λ¨μ μ μ΄λ€ κ²μ΄ μμκΉ?
λ¨μ μ μ€λ λ νμ λ무 λ§μ μμ μ€λ λλ₯Ό λ§λ€μ΄λλ€λ©΄ λ©λͺ¨λ¦¬ λλΉκ° μ¬ν΄μ§ μ μλ€λ μ μ΄λ€. κ·Έ λλ¬Έμ μΌλ§νΌμ μ€λ λκ° νμν μ§ μμΈ‘νκ³ ν λΉν΄μ μ¬μ©νλ κ²μ΄ μ€λ λ νμ νλͺ νκ² μ¬μ©νλ κ²μ΄λΌκ³ ν μ μλ€.
Java μμ μ€λ λ ν μ¬μ©νκΈ°
ExecutorService
μΈν°νμ΄μ€μ ꡬν κ°μ²΄λ₯Ό μ μ ν©ν 리 λ©μλλ‘ μ 곡νλ Executors
ν΄λμ€μ μΈ κ°μ§ λ©μλ μ€ νλλ₯Ό μ΄μ©νμ¬ μ€λ λ νμ μ½κ² μμ±ν μ μλ€. μμΈν λ©μλ μ¬μ©λ²μ μ΄κ³³μμ μ μ μλ€.
ThreadPoolExecutor
μμ μ¬μ©λλ νλΌλ―Έν°λ‘λ corePoolSize(μμ±ν κ°μ), maximumPoolSize(μμ±ν μ΅λ κ°μ), keepAliveTime(μ μ§ μκ°)
μ΄ μλ€. μ€λ λ νμμ μ€λ λλ₯Ό μμ±ν λ corePoolSize
μ νλΌλ―Έν°λ§νΌ μ½μ΄ μ€λ λλ₯Ό μμ±νλ€. κ·Έλ¦¬κ³ μλ‘μ΄ μμ
μ΄ λ€μ΄μ¬ λ λͺ¨λ μ½μ΄ μ€λ λκ° μ¬μ© μ€μ΄κ³ λ΄λΆ νκ° κ°λ μ°¨λ©΄ μ€λ λ νμ μ΅λ ν¬κΈ°κ° maximumPoolSize
λ§νΌ μ»€μ§ μ μλ€. λ§μ½ νμ¬ μ€λ λ νμ΄ corePoolSize
λ³΄λ€ λ§μ μ€λ λλ₯Ό κ°μ§κ³ μλ€λ©΄, μ΄κ³Όν μ€λ λμ λν΄μ keepAliveTime
νλΌλ―Έν°κ°λ³΄λ€ μ€λ«λμ ν μΌμ΄ μμΌλ©΄ μ κ±°λλ€. μ΄κ²μ μμμ λλΉλ₯Ό κ°μ μμΌ ν¨μ¨μ μΌλ‘ μ€λ λ νμ κ΄λ¦¬ν μ μκ² λλ€.
μ€λ λ νμ μμ
μμ²μ νλ λ°©μμ execute( )
, submit( )
λ°©μμ΄ μλ€. execute
λ°©μμ μμ
μ²λ¦¬ μ€μ μμΈκ° λ°μνλ©΄ ν΄λΉ μ€λ λκ° μ’
λ£λκ³ μ€λ λ νμμ μ κ±°ν λ€, μλ‘μ΄ μ€λ λλ₯Ό μμ±νμ¬ λ€λ₯Έ μμ
μ μ²λ¦¬νλ€. λν μ²λ¦¬κ²°κ³Όλ₯Ό λ°ννμ§ μλλ€. λ°λλ‘ submit
μ μμ
μ²λ¦¬ μ€μ μμΈκ° λ°μνλλΌλ μ€λ λκ° μ’
λ£λμ§ μκ³ λ€μ μμ
μ μ¬μ©λλ€. λν μ²λ¦¬ κ²°κ³Όλ₯Ό Future<?>
λ‘ λ°ννλ€. λ°λΌμ μ€λ λ νμ μ¬μ©ν λ submit
μ μ¬μ©νλ μ μ΄ λ λ°λμ§νλ€.
ν μ€νΈ ν΄λμ€λ₯Ό νλ μμ±νμ¬ ν μ€νΈλ₯Ό μ§ννλ€.
μ°μ MyCounter
ν΄λμ€λ₯Ό νλ λ§λ λ€. ν΄λΉ ν΄λμ€λ increment
λ©μλλ₯Ό ν΅ν΄ count
λ₯Ό μ¦κ°μν¨ ν 1μ΄μ λκΈ°μκ°μ κ°λλ€.
public class MyCounter {
private int count;
public void increment() {
try {
int temp = count;
count = temp + 1;
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public int getCount() {
return count;
}
}
첫 λ²μ§Έ λ°©λ²μ Executors.newFixedThreadPool(int nThreads)
μ λν΄ μμλ³Έλ€.
νλΌλ―Έν°λ‘ μ 곡λλ n κ° λ§νΌ μ€λ λ νμ μμ±νλ€. λ³΄ν΅ μΌμ λμ μ λ¬΄κ° λ°μν λ μ¬μ©νλ€. λ€μ μμλ 18κ°μ μ€λ λκ° νμν Task λ₯Ό μ 곡νκ³ 5κ°μ μ€λ λ νλ‘ μ²λ¦¬νλ κ³Όμ μ νμΈν΄ 보λ ν μ€νΈλ€.
@DisplayName("FixedThreadPool μ μμ±νλ€.")
@Test
void testCounterWithConcurrencyFixed() throws InterruptedException {
int numberOfThreads = 18;
ExecutorService service = Executors.newFixedThreadPool(5);
CountDownLatch latch = new CountDownLatch(numberOfThreads);
MyCounter counter = new MyCounter();
iterateThread(numberOfThreads, service, latch, counter);
assertThat(((ThreadPoolExecutor) service).getPoolSize()).isEqualTo(5);
}
private void iterateThread(int numberOfThreads, ExecutorService service, CountDownLatch latch, MyCounter counter) throws InterruptedException {
for (int i = 0; i < numberOfThreads; i++) {
service.submit(() -> {
counter.increment();
latch.countDown();
throw new IllegalArgumentException();
});
}
latch.await();
}
μ€ν κ²°κ³Όλ₯Ό μ΄ν΄λ³΄λ©΄ ν μ€λ λμμ μμ
μ μ²λ¦¬νλ λ° 1μ΄κ° 걸리λλ‘ μ€μ μ΄ λμ΄μλ€. κ·Έ λλ¬Έμ 5κ°μ μμ
μ 3λ², 3κ°μ μμ
μ 1λ² νκ² λλ©΄μ μ΄ μμ
μκ°μ (3+1)μ΄ + λ‘μ§ μ€ν μκ°(300ms)μ΄ λλ€. λ°λΌμ μ€λ λ νμ μμ±λ μ€λ λ κ°μλ§νΌ μμ
μ μ²λ¦¬νλ λͺ¨μ΅μ νμΈν μ μλ€. λν, μ€λ λ νμ ν¬κΈ°κ° 5κ°λ‘ μ μ§λλ λͺ¨μ΅μ νμΈν μ μλ€. ν΄λΉ μμ
μμ Count κ° μ¬λ¬ μ€λ λμμ λμμ νΈμΆλλλ° μ΄ κ²½μ° λΉλκΈ°λ‘ μ²λ¦¬λκΈ° λλ¬Έμ μ€μ Count μ κ²°κ³Ό(counter.getCount()
)λ 100λ³΄λ€ μμ κ°μ΄ λ°νλλ©° μ€νν λλ§λ€ λλ€ν κ²°κ³Όλ₯Ό λ°ννλ λͺ¨μ΅μ νμΈν μ μλ€.
λ λ²μ§Έ λ°©λ²μ Executors.newCachedThreadPool()
μ λν΄ μμλ³Έλ€.
μ΄κΈ° μ€λ λ κ°μκ° 0κ°λ‘ μ€μ λλ©° μ€λ λ κ°μλ³΄λ€ λ§μ μμ μμ μ μμ²λλ©΄ μλ‘μ΄ μ€λ λλ₯Ό μμ±νμ¬ μμ μ μ²λ¦¬νλ€. μμ μ΄ λλ μ€λ λκ° 60μ΄ μ΄μ μλ‘μ΄ μμ μμ²μ΄ μμΌλ©΄ μ€λ λλ₯Ό μ’ λ£νκ³ μ€λ λ νμμ μ κ±°λλ€. λ€μ μμλ 18κ°μ μ€λ λκ° νμν Task λ₯Ό μ 곡νκ³ 5κ°μ μ€λ λ νλ‘ μ²λ¦¬νλ κ³Όμ μ νμΈν΄ 보λ ν μ€νΈλ€. μμ μμ±ν΄λ μ½λμ μ΄μ΄μ μμ±ν΄μ ν μ€νΈλ₯Ό μ§ννλ©΄ λλ€.
@DisplayName("CachedThreadPool μ μμ±νλ€.")
@Test
void testCounterWithConcurrencyCached() throws InterruptedException {
int numberOfThreads = 18;
ExecutorService service = Executors.newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(numberOfThreads);
MyCounter counter = new MyCounter();
iterateThread(numberOfThreads, service, latch, counter);
assertThat(counter.getCount()).isEqualTo(numberOfThreads);
assertThat(((ThreadPoolExecutor) service).getPoolSize()).isEqualTo(18);
Thread.sleep(60000); // 60μ΄ ν μμ±λ μ€λ λκ° μ κ±°λλμ§ νμΈνλ€.
assertThat(((ThreadPoolExecutor) service).getPoolSize()).isEqualTo(0);
}
μΈ λ²μ§Έ λ°©λ²μ Executors.newScheduledThreadPool(int corePoolSize)
μ λν΄ μμλ³Έλ€.
μ€λ λλ₯Ό μΌμ μκ°μ΄ νλ₯΄κ³ λ λ€ μ€νμν€λλ‘ νλ μ€μΌμ€λ§ μ€λ λ κΈ°λ₯μ΄λ€. ν΄λΉ κΈ°λ₯μ ν
μ€νΈν΄ 보기 μν΄μλ ν
μ€νΈ μ½λκ° μλ λ©μΈμμ μ€νν΄λ΄μΌ νλ€. λ°λΌμ Sample
ν΄λμ€λ₯Ό λ§λ€κ³ μ€μ΅μ ν΄λ³Έλ€. corePoolSize λ μμ±ν corePool μ ν¬κΈ°λ₯Ό μ§μ ν΄μ£Όλ λΆλΆμΈλ° Executors.newScheduledThreadPool(0)
μ νλλΌλ μ€νμλ λ¬Έμ κ° μμ΄ λ³΄μΈλ€. λ€λ§ JDK 8 λ²μ μ΄νμμ λ°κ²¬λ λ²κ·Έλ‘ λ¨μΌ μ½μ΄ κ°μ λ¨Έμ μμ CPU λ₯Ό 100% μ¬μ©νλ λ²κ·Έκ° μκΈ° λλ¬Έμ νλΌλ―Έν°λ‘ 1 μ΄μμΌλ‘ μ€μ νλ€.
public class Sample {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
System.out.println("첫 λ²μ§Έ μμ
μ΄ μ€νλ©λλ€.");
service.schedule(() -> System.out.println("λ λ²μ§Έ μμ
μ΄ μ€νλ©λλ€."), 5, TimeUnit.SECONDS);
System.out.println("μΈ λ²μ§Έ μμ
μ΄ μ€νλ©λλ€.");
service.shutdown();
}
}
μ€μΌμ€μμ μ€μ ν 5μ΄κ° νλ₯Έ ν λ λ²μ§Έ μμ
μ΄ μ€νλ©λλ€.
κ° μ€νλλ λͺ¨μ΅μ νμΈν μ μλ€.
κ²°λ‘
μλ°μμ μ¬μ©νκ² λ μ€λ λ νμ λν΄ κ°λ¨νκ² μμλ΄€λ€. μΌλ§λ§νΌμ μ€λ λκ° μ£ΌκΈ°μ μΌλ‘ μλͺ¨λ μ§, μ€λ λλ₯Ό μΆκ°λ‘ μμ±ν μ§, μμ ν λ°ν κ°μ λ°μμ§μ λν λΆμμ ν λλ‘ μν©μ μ ν©ν Thread λ° ThreadPool μ μ μ©νλ€λ©΄ λ©ν° μ½μ΄ νλ‘κ·Έλλ°μ ν° λμμ΄ λλ€κ³ μκ°νλ€. λ€λ§, μ μ νμ§ μκ² μ¬μ©ν κ²½μ° μ€νλ € μ¬μ©νμ§ μμ κ²λ³΄λ€ λͺ»ν μ μκΈ° λλ¬Έμ μ£Όμν΄μΌ νλ€.
μ°Έκ³
- Class Executor Oracle Docs
- Introduction to Thread Pools in Java
- [Java] Thread Pool(μ€λ λ ν)
- Java Thread Pool (μλ° μ°λ λ ν)