0Pricing
Java Academy · 강의

작업 제출: Runnable과 Callable

Runnable과 Callable 작업을 제출하고 Future를 통해 결과를 가져옵니다.

작업 제출: Runnable과 Callable은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

실행 작업과 호출 가능 작업

ExecutorService에 제출하는 작업을 나타내는 함수형 인터페이스는 두 가지입니다.

  • 실행 작업: 반환값이 없으며 검사 예외를 선언할 수 없습니다.
  • 호출 가능 작업<V>: V 형식의 결과를 반환하며 검사 예외를 발생시킬 수 있습니다.
import java.util.concurrent.*;

// Runnable — no return value
Runnable r = () -> System.out.println("Running");

// Callable<Integer> — returns a value
Callable<Integer> c = () -> {
    return 42; // or do computation
};

submit(Runnable)

submit(Runnable)은 Future<?>를 반환합니다. 반환된 객체에서 get()을 호출하면 null이 반환되지만 작업이 완료되었음을 확인할 수 있습니다. 작업이 실패한 경우에는 예외가 발생합니다.

ExecutorService pool = Executors.newFixedThreadPool(2);

Future<?> future = pool.submit(() ->
    System.out.println("Task done on " + Thread.currentThread().getName())
);

future.get(); // waits for completion; returns null
System.out.println("Task confirmed complete");
pool.shutdown();

submit(Callable&lt;V&gt;)

submit(Callable)은 Future<V>를 반환합니다. 결과를 가져오려면 future.get()을 호출합니다. 작업이 완료될 때까지 블로킹됩니다.

ExecutorService pool = Executors.newFixedThreadPool(2);

Callable<Integer> sumTask = () -> {
    int sum = 0;
    for (int i = 1; i <= 100; i++) sum += i;
    return sum;
};

Future<Integer> future = pool.submit(sumTask);
Integer result = future.get(); // blocks until done
System.out.println("Sum 1..100 = " + result); // 5050
pool.shutdown();

invokeAll로 여러 호출 가능 작업 제출

invokeAll()은 호출 가능 작업의 컬렉션을 제출하고 Future 목록을 반환합니다. 반환되기 전에 모든 작업이 완료됩니다.

List<Callable<String>> tasks = List.of(
    () -> "Result A",
    () -> "Result B",
    () -> "Result C"
);

ExecutorService pool = Executors.newFixedThreadPool(3);
List<Future<String>> futures = pool.invokeAll(tasks);

for (Future<String> f : futures) {
    System.out.println(f.get());
}
pool.shutdown();

invokeAny: 첫 번째 성공 결과

invokeAny()는 호출 가능 작업의 컬렉션을 제출하고, 성공적으로 완료된 첫 번째 작업의 결과를 반환하며 나머지는 취소합니다.

List<Callable<String>> candidates = List.of(
    () -> { Thread.sleep(200); return "Slow"; },
    () -> { Thread.sleep(50);  return "Fast"; },
    () -> { Thread.sleep(100); return "Medium"; }
);

ExecutorService pool = Executors.newFixedThreadPool(3);
String winner = pool.invokeAny(candidates);
System.out.println("First: " + winner); // Fast
pool.shutdown();

execute()와 submit() 비교

execute(Runnable)은 실행 후 잊어버리는 방식으로 동작합니다. Future를 반환하지 않으며, 포착되지 않은 예외 처리기를 추가하지 않으면 예외가 사라집니다. submit()은 작업을 Future로 감싸 예외를 저장하고, get()을 통해 가져올 수 있게 합니다.

ExecutorService pool = Executors.newFixedThreadPool(2);

// execute: exception silently lost
pool.execute(() -> { throw new RuntimeException("Oops!"); });

// submit: exception captured in Future
Future<?> f = pool.submit(() -> { throw new RuntimeException("Oops!"); });
try {
    f.get();
} catch (ExecutionException e) {
    System.out.println("Caught: " + e.getCause().getMessage());
}
pool.shutdown();

검사 예외를 발생시키는 호출 가능 작업

실행 작업과 달리 호출 가능 작업은 검사 예외를 발생시킬 수 있으므로 입출력 및 데이터베이스 작업에 자연스럽게 사용할 수 있습니다.

Callable<String> dbQuery = () -> {
    // This compiles fine — checked exception is declared on Callable.call()
    if (Math.random() < 0.5) throw new java.sql.SQLException("DB error");
    return "row data";
};

Future<String> f = pool.submit(dbQuery);
try {
    String data = f.get();
} catch (ExecutionException e) {
    if (e.getCause() instanceof java.sql.SQLException) {
        System.out.println("DB failed: " + e.getCause().getMessage());
    }
}

독립적인 계산의 병렬화

서로 독립적인 계산을 병렬로 실행하려면 여러 호출 가능 작업을 제출합니다.

ExecutorService pool = Executors.newFixedThreadPool(4);

Future<Long> sumFuture  = pool.submit(() -> LongStream.rangeClosed(1,1_000_000).sum());
Future<Long> prodFuture = pool.submit(() -> LongStream.rangeClosed(1,20).reduce(1L, (a,b)->a*b));

long sum  = sumFuture.get();
long prod = prodFuture.get();
System.out.println("Sum: " + sum + ", 20!: " + prod);
pool.shutdown();

호출 가능 작업 결과 집계

많은 작업을 제출한 다음 모든 작업이 완료되면 결과를 집계합니다.

int N = 8;
ExecutorService pool = Executors.newFixedThreadPool(N);
List<Future<Integer>> futures = new ArrayList<>();

for (int i = 0; i < N; i++) {
    final int chunk = i;
    futures.add(pool.submit(() -> chunk * chunk)); // i^2
}

int total = 0;
for (Future<Integer> f : futures) total += f.get();
System.out.println("Sum of squares: " + total);
pool.shutdown();

호출 가능 작업의 시간 제한

무한히 기다리지 않도록 future.get(timeout, unit)에 시간 제한을 전달합니다.

Future<String> f = pool.submit(() -> {
    Thread.sleep(5000); // slow task
    return "done";
});
try {
    String result = f.get(1, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    System.out.println("Task timed out");
    f.cancel(true); // interrupt the task
} catch (ExecutionException e) {
    System.out.println("Task failed: " + e.getCause());
}

권장 사항

권장 사항을 요약하면 다음과 같습니다.

  • execute()보다 submit()을 사용합니다. 예외가 저장됩니다.
  • 반환값이나 검사 예외가 필요하면 호출 가능 작업을 사용합니다.
  • 항상 시간 제한과 함께 future.get()을 호출합니다.
  • 일괄 처리에는 invokeAll을 사용하고, 첫 번째 응답이 성공하면 되는 경우에는 invokeAny를 사용합니다.
  • 항상 풀을 종료합니다.

확인 문제

실행 작업에 비해 호출 가능 작업이 갖는 핵심 장점은 무엇입니까?

복습: 실행 작업과 호출 가능 작업

핵심 내용은 다음과 같습니다.

  • 실행 작업: 반환값이 없고 검사 예외를 발생시킬 수 없으며, submit은 null인 Future<?>를 반환합니다.
  • 호출 가능 작업: V를 반환하고 검사 예외를 발생시킬 수 있으며, submit은 Future<V>를 반환합니다.
  • invokeAll: 작업 묶음을 제출하고 모두 완료될 때까지 기다립니다.
  • invokeAny: 성공한 첫 번째 결과를 반환하고 나머지는 취소합니다.
  • 예외를 포착하려면 항상 submit()을 사용합니다.

자주 묻는 질문

“작업 제출: Runnable과 Callable” 강의는 무료인가요?

네 — “작업 제출: Runnable과 Callable” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“작업 제출: Runnable과 Callable”에서 뭘 배우나요?

Runnable과 Callable 작업을 제출하고 Future를 통해 결과를 가져옵니다. 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Java Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“작업 제출: Runnable과 Callable” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. ExecutorService와 스레드 풀 유형
  2. 작업 제출: Runnable과 Callable
  3. Future와 오류 처리
  4. 반복 작업을 위한 ScheduledExecutorService
← Java Academy(으)로 돌아가기