Executor — 제출된 Runnable 태스크를 실행하는 객체
Executor — 제출된 Runnable 태스크를 실행하는 객체
Executor는 제출된 Runnable 태스크를 실행하는 객체를 나타내는 인터페이스예요. 태스크 제출을 실행 방식(스레드 사용, 스케줄링 등)에서 분리하고, 명시적으로 new Thread(...)를 만들지 않도록 돕는 핵심 추상화예요.
본문
개념 이해하기
Executor는 제출된 Runnable 태스크를 실행하는 객체예요. 태스크 제출을 각 태스크가 어떻게 실행될지(스레드 사용, 스케줄링 등)의 세부 사항에서 분리해요. 보통 new Thread(...).start() 대신 사용해요.
public interface Executor
예를 들어, 각 태스크마다 new Thread(new RunnableTask()).start()를 호출하는 대신:
Executor executor = anExecutor();
executor.execute(new RunnableTask1());
executor.execute(new RunnableTask2());
...
Executor는 반드시 비동기 실행을 요구하지는 않아요. 가장 단순한 경우 호출자 스레드에서 즉시 실행할 수 있어요:
class DirectExecutor implements Executor {
public void execute(Runnable r) { r.run(); }
}
좀 더 흔하게는 태스크를 호출자 스레드가 아닌 다른 스레드에서 실행해요. 스레드마다 새 스레드를 만드는 예:
class ThreadPerTaskExecutor implements Executor {
public void execute(Runnable r) { new Thread(r).start(); }
}
많은 구현이 언제 어떻게 태스크를 스케줄할지 제한을 둬요. 아래는 제출을 두 번째 executor에 직렬화하는 복합 executor 예시예요:
class SerialExecutor implements Executor {
final Queue<Runnable> tasks = new ArrayDeque<>();
final Executor executor;
Runnable active;
SerialExecutor(Executor executor) { this.executor = executor; }
public synchronized void execute(Runnable r) {
tasks.add(() -> {
try { r.run(); } finally { scheduleNext(); }
});
if (active == null) { scheduleNext(); }
}
protected synchronized void scheduleNext() {
if ((active = tasks.poll()) != null) { executor.execute(active); }
}
}
이 패키지에 제공되는 Executor 구현들은 더 광범위한 인터페이스인 ExecutorService를 구현해요. ThreadPoolExecutor는 확장 가능한 스레드 풀 구현, Executors는 이 executor들에 대한 편리한 팩토리 메서드를 제공해요.
메모리 일관성 효과: 한 스레드에서 Executor에 Runnable 객체를 제출하기 전의 동작은, 아마도 다른 스레드에서 일어나는 그 실행이 시작되기 전에 happen-before 관계예요.
메서드
void execute(Runnable command)
주어진 커맨드를 미래의 어느 시점에 실행해요. 커맨드는 구현의 판단에 따라 새 스레드, 풀 스레드, 또는 호출 스레드에서 실행될 수 있어요.
command— 실행할 태스크RejectedExecutionException— 태스크를 실행으로 받아들일 수 없을 때NullPointerException—command가null일 때