Thread pool
Thread pool은 작업 처리에 사용되는 스레드를 제한된 개수만큼 정해 놓고 작업 큐(Queue)에 들어오는 작업들을 하나씩 스레드가 맡아 처리하는 것을 말한다.
=> 자원 효율성, 작업 처리 속도 향상(대기 중인 쓰레드 활용), 작업 제어(:동시 처리 가능한 개수 미리 지정)
Java에서는 Thread pool 을 구현하기 위해 Executor 및 ExecutorService 인터페이스를 사용한다.
ExecutorService
ExecutorService는 비동기 모드에서 작업 실행을 간소화하는 JDK API입니다. 일반적으로 ExecutorService는 스레드 풀과 작업 (Runnable, Callable) 할당을 위한 API를 제공합니다.
Runnable : 결과를 반환받을 수 없는 단일 run() 메서드가 있다.
Callable : Runnable의 발전된 형태로, 결과를 받을 수 있다.
public class RunnableSample implements Runnable {
@Override
public void run() {
// 스레드로 실행할 코드
System.out.println("rannable 의 run 메서드 실행");
}
}
public class RunnableTest {
public static void main(String[] args) {
RunnableSample runnable = new RunnableSample();
Thread thread = new Thread(runnable);
thread.start();
}
}
=> Runnable 인터페이스를 사용함으로 인해...
1. 비즈니스 로직과 스레드의 구분
- Runnable 인터페이스: 로직을 담고 있는 클래스는 스레드 실행에만 필요한 코드를 Runnable로 구현하고, 실행은 Thread 객체가 담당합니다.
2. 코드 재사용성
- 하나의 Runnable 객체를 여러 Thread에서 사용 가능합니다.
3. 다중 상속 제한의 한계 극복
- 다른 클래스를 상속받고 있는 경우: 자바의 다중 상속 제한 때문에 Thread 클래스를 상속받을 수 없을 때 Runnable을 사용합니다.
4. 메모리 효율성
- Thread 클래스를 상속받으면 각각의 스레드는 별도의 Thread 인스턴스를 생성하지만, Runnable 인터페이스를 구현함으로 여러 스레드가 같은 Runnable 객체를 공유할 수 있습니다.
ExecutorService 인스턴스화
1.1. Factory Methods of the Executors Class
Executor, ExecutorService는 모두 쓰레드 풀을 위한 인터페이스입니다.
직접 쓰레드를 다루는 것은 번거로우므로, 이를 도와주는 팩토리 클래스인 Executors가 등장하게 되었습니다.
보다 쉽게 ExecutorService 를 만들기 위해서는 Executors 클래스의 팩터리 메소드 중 하나인 newFixedThreadPool() 를 사용합니다.
ExecutorService executorService = Executors.newFixedThreadPool(10); // 쓰레드의 개수 지정 가능
1.2. Assigning Tasks to the ExecutorService
Executor인터페이스에서 상속되는 execute()와 submit(), invokeAny() 및 invokeAll()을 포함한 여러 방법을 사용하여 작업을 ExecutorService에 할당할 수 있다.
executorService.execute(runnableTask);
Future<String> future = executorService.submit(callableTask);
1.3 Shutting Down an ExecutorService
작업이 없어도 ExecutorService 는 자동적으로 파괴되지 않고, 새로운 일을 하기 위해 계속 살아있는 상태다.
이를 종료하는 구문을 작성해줘야 하는데,
ExecutorService를 제대로 종료하려면 shutdown() API와 shutdownNow() API를 사용해준다.
하지만 shutdown() 메서드가 ExecutorService를 즉시 파괴하지는 않는다.
이렇게 하면 실행 중인 모든 스레드가 현재 작업을 완료한 후 ExecutorService가 새 작업 수락을 중단하고 종료됨.
executorService.shutdown();
shutdownNow() 메서드는 실행자 서비스를 즉시 파괴하려고 하지만 실행 중인 모든 스레드가 동시에 중단된다는 보장은 없다.
List<Runnable> notExecutedTasks = executorService.shutDownNow();
이 방법은 처리 대기 중인 작업 목록을 반환함. 이러한 작업을 어떻게 처리할지는 개발자의 결정에 달려 있음.
오라클에서도 권장하는 실행자 서비스를 종료하는 좋은 방법 중 하나는 다음 두 가지 방법을 모두 awaitTermination() 메서드와 결합하여 사용하는 것이다:
executorService.shutdown(); // 중단
try {
if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) { // 일정기간 기다림
executorService.shutdownNow(); // 중단
}
} catch (InterruptedException e) {
executorService.shutdownNow();
}
이 접근 방식을 사용하면 ExecutorService 는 먼저 새 작업 수행을 중단한 다음 모든 작업이 완료될 때까지 지정된 기간까지 기다립니다. 해당 기간이 만료되면 실행이 즉시 중단됩니다.
https://www.baeldung.com/java-executor-service-tutorial
'BackEnd > JAVA' 카테고리의 다른 글
서블릿 컨테이너의 ServletContext 동작 방식 / HttpSession (1) | 2025.01.20 |
---|---|
[JAVA] Calendar 클래스 / 오늘 날짜 구하기 / 이번 달의 첫번째 날 / 이번 달의 마지막 날 / 현재로부터 과거까지 역순 데이트 리스트 출력 (0) | 2023.05.10 |
[JAVA] POJO (Plain Old Java Object) (0) | 2023.03.07 |
[JAVA] 콤마로 구분되어 저장된 String 데이터 List화 하기 (0) | 2023.02.21 |
[JAVA] 클래스(class)와 생성자 (1) | 2023.01.14 |