Java의 Executor
인터페이스
Executor
는 Java의 멀티스레드 프로그래밍을 단순화하기 위해 설계된 인터페이스입니다. 이 인터페이스는 스레드 생성을 개발자가 직접 처리하지 않고, 작업 제출 및 실행을 처리할 수 있도록 추상화된 방법을 제공합니다.
특징
- 작업 제출과 실행 전략 분리:
Executor
를 사용하면 작업을 스레드 풀이나 특정 실행 정책에 맞게 실행할 수 있습니다. - 스레드 관리 최적화: 스레드 풀을 사용하면 스레드의 재사용과 리소스 관리를 효율적으로 처리할 수 있습니다.
- 비동기 작업 처리: 작업 실행이 비동기로 처리될 수 있습니다.
Executor
인터페이스 정의
@FunctionalInterface
public interface Executor {
void execute(Runnable command);
}
execute(Runnable command)
:Runnable
객체를 받아 실행합니다. 작업 실행 방식은 구현에 따라 달라집니다.
Executor
의 주요 구현체
ThreadPoolExecutor
: 스레드 풀을 사용하여 작업을 실행.ScheduledThreadPoolExecutor
: 일정한 시간 간격으로 작업을 실행.ForkJoinPool
: 작업을 작은 단위로 나누어 병렬 처리.
Executor
사용 예제
1. 간단한 구현
import java.util.concurrent.Executor;
public class SimpleExecutor implements Executor {
@Override
public void execute(Runnable command) {
new Thread(command).start(); // 매번 새로운 스레드를 생성하여 실행
}
public static void main(String[] args) {
Executor executor = new SimpleExecutor();
// 작업 제출
executor.execute(() -> System.out.println("Hello from SimpleExecutor!"));
}
}
- 설명:
SimpleExecutor
는 매번 새 스레드를 생성해 작업을 실행합니다.
2. 스레드 풀 사용
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
// 고정 크기 스레드 풀 생성
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
int taskId = i;
executorService.execute(() -> {
System.out.println("Task " + taskId + " is running in thread " + Thread.currentThread().getName());
});
}
// 스레드 풀 종료
executorService.shutdown();
}
}
- 설명:
Executors.newFixedThreadPool(3)
: 3개의 스레드로 구성된 스레드 풀 생성.execute()
: 작업을 스레드 풀에 제출하여 실행.
3. 사용자 정의 스레드 풀
import java.util.concurrent.*;
public class CustomThreadPoolExecutor {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // corePoolSize
5, // maximumPoolSize
60, // keepAliveTime
TimeUnit.SECONDS,// keepAliveTime 단위
new LinkedBlockingQueue<>(10) // 작업 대기열
);
for (int i = 0; i < 15; i++) {
int taskId = i;
executor.execute(() -> {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
});
}
executor.shutdown();
}
}
- 설명:
corePoolSize
: 기본적으로 유지되는 스레드 수.maximumPoolSize
: 최대 생성 가능한 스레드 수.keepAliveTime
: 스레드가 사용되지 않을 때 유지되는 시간.
4. ScheduledExecutorService
사용
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorExample {
public static void main(String[] args) {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// 3초 후에 실행
scheduler.schedule(() -> System.out.println("Task executed after 3 seconds"), 3, TimeUnit.SECONDS);
// 2초 주기로 반복 실행
scheduler.scheduleAtFixedRate(() -> System.out.println("Repeating task"), 1, 2, TimeUnit.SECONDS);
// 10초 후 스케줄러 종료
scheduler.schedule(() -> {
System.out.println("Shutting down scheduler");
scheduler.shutdown();
}, 10, TimeUnit.SECONDS);
}
}
- 설명:
schedule()
: 특정 시간 후 작업 실행.scheduleAtFixedRate()
: 고정 주기로 작업 반복 실행.
ExecutorService
와 Executor
의 차이
Executor
: 단순히 작업 실행에 대한 API만 제공합니다.ExecutorService
:- 작업 제출 및 결과 관리.
- 스레드 종료 (
shutdown()
,shutdownNow()
). - 작업 결과 반환 (
Future
).
Executor
를 사용하는 이유
- 스레드 관리: 스레드 생성과 관리를 직접 처리하지 않아도 됩니다.
- 효율적인 자원 활용: 스레드 재사용 및 작업 큐를 통해 효율적인 작업 처리.
- 다양한 실행 정책: 단일 스레드, 스레드 풀, 스케줄링 등 다양한 방식으로 작업 처리 가능.
위의 예제 코드는 다양한 상황에서 Executor
를 활용하는 방법을 보여줍니다. Executor
를 잘 활용하면 멀티스레드 프로그래밍의 복잡성을 크게 줄일 수 있습니다.
'Java' 카테고리의 다른 글
JMS(Java Message Service) (0) | 2024.11.07 |
---|---|
Stream (0) | 2024.11.05 |
jar, war 설명과 차이 (0) | 2024.10.09 |
Shallow copy와 Deep copy (0) | 2024.09.19 |
어그리게이션(Aggregation)과 컴포지션(Composition) (0) | 2024.08.21 |