0Pricing
Java Academy · Lekcja

ScheduledExecutorService do zadań cyklicznych

Planuj zadania ze stałą częstotliwością lub stałymi opóźnieniami na potrzeby odpytywania i prac konserwacyjnych w tle.

ScheduledExecutorService do zadań cyklicznych to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 4 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.

Dlaczego używać ScheduledExecutorService?

ScheduledExecutorService rozszerza ExecutorService, aby obsługiwać planowanie zadań. Zastępuje starsze klasy Timer/TimerTask bezpiecznym pod względem wyjątków harmonogramem opartym na puli wątków.

import java.util.concurrent.*;

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);

// One-shot delay
scheduler.schedule(
    () -> System.out.println("Runs once after 3 seconds"),
    3, TimeUnit.SECONDS
);

// Don't forget to shut down eventually:
// scheduler.shutdown();

schedule: jednorazowe opóźnienie

schedule(task, delay, unit) uruchamia zadanie jeden raz po upływie określonego opóźnienia. Działa zarówno z Runnable, jak i Callable:

ScheduledExecutorService s = Executors.newScheduledThreadPool(1);

// Runnable:
ScheduledFuture<?> sf1 = s.schedule(
    () -> System.out.println("Runnable fired!"), 2, TimeUnit.SECONDS);

// Callable:
ScheduledFuture<Integer> sf2 = s.schedule(
    () -> { System.out.println("Callable fired!"); return 42; },
    2, TimeUnit.SECONDS);

System.out.println(sf2.get()); // 42 (after 2 seconds)

scheduleAtFixedRate

scheduleAtFixedRate(task, initialDelay, period, unit) uruchamia zadanie wielokrotnie ze stałą częstotliwością. Jeśli wykonanie zadania trwa dłużej niż okres, następne wykonanie rozpoczyna się natychmiast po jego zakończeniu (bez nakładania się wykonań).

ScheduledExecutorService s = Executors.newScheduledThreadPool(1);
AtomicInteger count = new AtomicInteger();

ScheduledFuture<?> sf = s.scheduleAtFixedRate(
    () -> System.out.println("Tick " + count.incrementAndGet()),
    0,   // initial delay
    1,   // period
    TimeUnit.SECONDS
);

Thread.sleep(5000);
sf.cancel(false); // stop after 5 ticks
s.shutdown();

scheduleWithFixedDelay

scheduleWithFixedDelay(task, initialDelay, delay, unit) po każdym zakończeniu zadania czeka przez określone opóźnienie, zanim rozpocznie następne. Dobrze nadaje się do odpytywania, gdy czas trwania zadania jest zmienny.

s.scheduleWithFixedDelay(
    () -> {
        System.out.println("Polling... " + Instant.now());
        // variable-length polling task
    },
    0,  // no initial delay
    5,  // 5-second gap AFTER each completion
    TimeUnit.SECONDS
);
// Next execution = task end + 5 seconds (not task start)

Stała częstotliwość a stałe opóźnienie

Najważniejsza różnica:

  • scheduleAtFixedRate: następne rozpoczęcie = ostatnie rozpoczęcie + okres (odstępy czasowe bez narastającego przesunięcia)
  • scheduleWithFixedDelay: następne rozpoczęcie = ostatnie zakończenie + opóźnienie (gwarantowany odstęp między wykonaniami)

Używaj fixedRate dla sygnałów heartbeat, a fixedDelay do odpytywania, gdy czas trwania zadania jest zmienny.

ScheduledFuture: anulowanie

schedule/scheduleAtFixedRate/scheduleWithFixedDelay zwracają obiekt ScheduledFuture. Wywołaj cancel(false), aby zatrzymać przyszłe wykonania bez przerywania aktualnie wykonywanego zadania:

ScheduledFuture<?> sf = scheduler.scheduleAtFixedRate(
    () -> System.out.println("Heartbeat"), 0, 1, TimeUnit.SECONDS);

// Cancel after 5 seconds
scheduler.schedule(() -> sf.cancel(false), 5, TimeUnit.SECONDS);

Obsługa wyjątków w zadaniach zaplanowanych

Jeśli zaplanowane zadanie zgłosi nieprzechwycony wyjątek, harmonogram po cichu zatrzymuje wykonywanie cykliczne — kolejne wykonania nie nastąpią. Zawsze używaj try-catch:

scheduler.scheduleAtFixedRate(() -> {
    try {
        // risky work
        if (Math.random() < 0.3) throw new RuntimeException("transient");
        System.out.println("Success");
    } catch (Exception e) {
        System.err.println("Task error: " + e.getMessage());
        // task continues to run on next interval
    }
}, 0, 2, TimeUnit.SECONDS);

Praktyczny przykład: odświeżanie pamięci podręcznej

Użyj zaplanowanego zadania, aby okresowo odświeżać pamięć podręczną:

class PriceCache {
    private volatile Map<String, Double> prices = new HashMap<>();
    
    PriceCache(ScheduledExecutorService scheduler) {
        scheduler.scheduleAtFixedRate(this::refresh, 0, 30, TimeUnit.SECONDS);
    }
    
    private void refresh() {
        System.out.println("Refreshing prices...");
        // prices = fetchFromDatabase();
    }
    
    Double getPrice(String symbol) { return prices.get(symbol); }
}

Praktyczny przykład: ponawianie prób z narastającym opóźnieniem

Implementacja ponawiania prób z wykładniczym zwiększaniem opóźnienia przy użyciu zadań planowanych:

void retryWithBackoff(Runnable task, int maxRetries, ScheduledExecutorService s) {
    AtomicInteger attempts = new AtomicInteger(0);
    long[] delay = {1}; // seconds
    
    Runnable wrapper = new Runnable() {
        public void run() {
            try {
                task.run();
            } catch (Exception e) {
                int n = attempts.incrementAndGet();
                if (n < maxRetries) {
                    delay[0] *= 2;
                    s.schedule(this, delay[0], TimeUnit.SECONDS);
                } else System.err.println("Max retries reached");
            }
        }
    };
    s.execute(wrapper);
}

Monitorowanie: getDelay i getQueue

Inspekcja stanu harmonogramu:

ScheduledFuture<?> sf = scheduler.schedule(() -> {}, 10, TimeUnit.SECONDS);
System.out.println("Delay remaining: " + sf.getDelay(TimeUnit.SECONDS) + "s");

// ThreadPoolExecutor provides queue inspection:
if (scheduler instanceof ScheduledThreadPoolExecutor ste) {
    System.out.println("Queue size: " + ste.getQueue().size());
}

Zamykanie i opróżnianie

Podczas zamykania należy zdecydować, czy zaplanowane zadania powinny nadal być wykonywane:

ScheduledThreadPoolExecutor ste = (ScheduledThreadPoolExecutor)
    Executors.newScheduledThreadPool(2);

// Cancel scheduled tasks on shutdown (default: false = they run)
ste.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
ste.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);

ste.shutdown();

Szybkie sprawdzenie

Zadanie zaplanowane za pomocą scheduleAtFixedRate(..., 1, TimeUnit.SECONDS) trwa 3 sekundy. Jak harmonogram obsłuży taką sytuację?

Podsumowanie: ScheduledExecutorService

Najważniejsze informacje:

  • schedule() — jednorazowe wykonanie z opóźnieniem
  • scheduleAtFixedRate() — taktowanie bez dryfu (od rozpoczęcia do rozpoczęcia)
  • scheduleWithFixedDelay() — gwarantowana przerwa (od zakończenia do rozpoczęcia)
  • Nieobsłużone wyjątki po cichu zatrzymują cykliczne zadania — należy zawsze opakować je w try-catch
  • Anulowanie za pomocą ScheduledFuture.cancel(false)

Często zadawane pytania

Czy lekcja „ScheduledExecutorService do zadań cyklicznych” jest bezpłatna?

Tak — pełny tekst „ScheduledExecutorService do zadań cyklicznych” 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 „ScheduledExecutorService do zadań cyklicznych”?

Planuj zadania ze stałą częstotliwością lub stałymi opóźnieniami na potrzeby odpytywania i prac konserwacyjnych w tle. Ć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 4 z 4.

Ile czasu zajmuje lekcja „ScheduledExecutorService do zadań cyklicznych”?

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