0Pricing
Java Academy · Lekcja

Przesyłanie zadań: Runnable a Callable

Przesyłaj zadania Runnable i Callable oraz pobieraj wyniki za pośrednictwem Future.

Przesyłanie zadań: Runnable a Callable to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.

Runnable a Callable

Dwa interfejsy funkcyjne reprezentują zadania przesyłane do ExecutorService:

  • Runnable: nie zwraca wartości i nie może deklarować wyjątków kontrolowanych
  • Callable<V>: zwraca wynik typu V i może zgłaszać wyjątki kontrolowane
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) zwraca obiekt Future<?>. Wywołanie na nim get() zwraca null, ale potwierdza zakończenie zadania (lub zgłasza wyjątek, jeśli zadanie się nie powiodło).

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<V>)

submit(Callable) zwraca obiekt Future<V>. Wywołaj future.get(), aby pobrać wynik (metoda blokuje wykonanie do czasu zakończenia zadania).

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();

Przesyłanie wielu obiektów Callable za pomocą invokeAll

invokeAll() przesyła kolekcję obiektów Callable i zwraca List obiektów Future — wszystkie zadania zostają ukończone przed zwróceniem wyniku:

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: pierwszy pomyślny wynik

invokeAny() przesyła kolekcję obiektów Callable i zwraca wynik pierwszego zadania, które pomyślnie się zakończy, anulując pozostałe:

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() a submit()

execute(Runnable) uruchamia zadanie i nie oczekuje na jego wynik — nie zwraca obiektu Future, a wyjątki są tracone, chyba że zostanie dodany moduł obsługi nieprzechwyconych wyjątków. submit() opakowuje zadanie w obiekt Future, przechwytując wyjątki, które można pobrać za pomocą 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 z wyjątkami kontrolowanymi

W przeciwieństwie do Runnable, Callable może zgłaszać wyjątki kontrolowane — dzięki temu dobrze nadaje się do operacji wejścia-wyjścia i operacji na bazie danych:

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());
    }
}

Równoległe wykonywanie niezależnych obliczeń

Użyj wielu przesłań Callable, aby wykonywać niezależne obliczenia równolegle:

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();

Agregowanie wyników Callable

Prześlij wiele zadań i agreguj wyniki po zakończeniu wszystkich:

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();

Limit czasu dla Callable

Przekaż limit czasu do future.get(timeout, unit), aby uniknąć oczekiwania w nieskończoność:

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());
}

Najlepsze praktyki

Podsumowanie najlepszych praktyk:

  • Używaj submit() zamiast execute() — wyjątki są przechwytywane
  • Używaj Callable, gdy potrzebujesz wartości zwracanej lub wyjątków kontrolowanych
  • Zawsze wywołuj future.get() z limitem czasu
  • Używaj invokeAll do przetwarzania wsadowego, a invokeAny, gdy wygrywa pierwsza odpowiedź
  • Zawsze wyłączaj pulę

Szybkie sprawdzenie

Jaka jest najważniejsza przewaga Callable nad Runnable?

Podsumowanie: Runnable a Callable

Najważniejsze informacje:

  • Runnable: void, bez wyjątków kontrolowanych; submit zwraca Future o wartości null
  • Callable: zwraca V, może zgłaszać wyjątki kontrolowane; submit zwraca Future
  • invokeAll: przesyła partię zadań i czeka na zakończenie wszystkich
  • invokeAny: zwraca pierwszy pomyślny wynik i anuluje pozostałe zadania
  • Zawsze używaj submit() do przechwytywania wyjątków

Często zadawane pytania

Czy lekcja „Przesyłanie zadań: Runnable a Callable” jest bezpłatna?

Tak — pełny tekst „Przesyłanie zadań: Runnable a Callable” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Przesyłanie zadań: Runnable a Callable”?

Przesyłaj zadania Runnable i Callable oraz pobieraj wyniki za pośrednictwem Future. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Java Academy?

Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Przesyłanie zadań: Runnable a Callable”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?

Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. ExecutorService i typy pul wątków
  2. Przesyłanie zadań: Runnable a Callable
  3. Future i obsługa błędów
  4. ScheduledExecutorService do zadań cyklicznych
← Powrót do Java Academy