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()zamiastexecute()— 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
- ExecutorService i typy pul wątków
- Przesyłanie zadań: Runnable a Callable
- Future i obsługa błędów
- ScheduledExecutorService do zadań cyklicznych