Java Academy · Oppitunti

Future ja virheenkäsittely

Käytä Future.get-metodia aikakatkaisujen kanssa, käsittele ExecutionException ja peruuta käynnissä olevia tehtäviä.

Oppitunti 3/413 vaihetta

Future ja virheenkäsittely on ilmainen Java Academy-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Java Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Java Academy-kurssilla on yhteensä 4 oppituntia.

Mikä on Future?

Future<V> edustaa asynkronisen laskennan odottavaa tulosta. Se tarjoaa metodeja valmistumisen tarkistamiseen, tuloksen odottamiseen ja virheiden käsittelyyn.

import java.util.concurrent.*;

ExecutorService pool = Executors.newFixedThreadPool(2);
Future<Integer> future = pool.submit(() -> {
    Thread.sleep(1000);
    return 42;
});

System.out.println("Is done? " + future.isDone()); // false
Integer result = future.get(); // blocks
System.out.println("Result: " + result); // 42
pool.shutdown();

future.get() aikakatkaisulla

Käyttäkää aina mieluummin aikakatkaisullista versiota, jotta suoritus ei jää odottamaan loputtomasti:

try {
    Integer result = future.get(5, TimeUnit.SECONDS);
    System.out.println(result);
} catch (TimeoutException e) {
    System.out.println("Timed out");
    future.cancel(true); // interrupt the task
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
} catch (ExecutionException e) {
    System.out.println("Task threw: " + e.getCause());
}

ExecutionException

Jos tehtävä heitti poikkeuksen, future.get() käärii sen ExecutionException-poikkeukseen. Purkakaa kääre getCause()-metodilla:

Future<String> failing = pool.submit(() -> {
    throw new IllegalArgumentException("bad input");
});

try {
    failing.get();
} catch (ExecutionException e) {
    Throwable cause = e.getCause();
    System.out.println(cause.getClass().getSimpleName()); // IllegalArgumentException
    System.out.println(cause.getMessage()); // bad input
}

Futuren peruuttaminen

cancel(mayInterruptIfRunning) yrittää peruuttaa tehtävän. Se palauttaa arvon true, jos peruutus onnistui. Jos mayInterruptIfRunning on true, käynnissä olevalle säikeelle lähetetään keskeytys.

Future<String> f = pool.submit(() -> {
    try {
        Thread.sleep(10_000); // long task
        return "done";
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        return "interrupted";
    }
});

boolean cancelled = f.cancel(true); // send interrupt
System.out.println("Cancelled: " + cancelled); // true
System.out.println("Is cancelled: " + f.isCancelled()); // true

isCancelled ja isDone

isDone() palauttaa arvon true, jos tehtävä valmistui normaalisti, heitti poikkeuksen tai peruutettiin. isCancelled() palauttaa arvon true vain, jos tehtävä peruutettiin.

// After cancel:
System.out.println(f.isDone());       // true
System.out.println(f.isCancelled()); // true

// After normal completion:
Future<Integer> done = pool.submit(() -> 5);
done.get(); // wait
System.out.println(done.isDone());       // true
System.out.println(done.isCancelled()); // false

CompletionService ensimmäisenä valmistuville tuloksille

ExecutorCompletionService käärii poolin ja tarjoaa take()-metodin seuraavan valmistuneen Futuren noutamiseen (valmistumisjärjestyksessä, ei lähetysjärjestyksessä):

ExecutorCompletionService<Integer> cs =
    new ExecutorCompletionService<>(pool);

for (int i = 0; i < 5; i++) {
    final int delay = 5 - i; // submit fastest last
    cs.submit(() -> { Thread.sleep(delay * 100); return delay; });
}

for (int i = 0; i < 5; i++) {
    Future<Integer> f = cs.take(); // next completed
    System.out.println(f.get());
}

Useiden Future-olioiden turvallinen käsittely

Kun keräätte tuloksia useista Future-olioista, käykää ne läpi ja käsitelkää poikkeukset kunkin Futuren kohdalla erikseen:

List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 5; i++) {
    final int id = i;
    futures.add(pool.submit(() ->
        id % 2 == 0 ? "ok_" + id : (() -> { throw new RuntimeException("fail_"+id); }).get()
    ));
}

for (Future<String> f : futures) {
    try {
        System.out.println(f.get());
    } catch (ExecutionException e) {
        System.out.println("Error: " + e.getCause().getMessage());
    }
}

Future.get() poolin sulkemisen jälkeen

Lähettäminen shutdown()-metodin kutsumisen jälkeen heittää RejectedExecutionException-poikkeuksen. Ennen sulkemista lähetettyjen Future-olioiden tulokset voidaan kuitenkin noutaa sulkemisen jälkeen:

ExecutorService pool2 = Executors.newFixedThreadPool(2);
Future<Integer> f = pool2.submit(() -> 99);
pool2.shutdown(); // no new tasks

// Still valid — task was submitted before shutdown:
System.out.println(f.get()); // 99

try {
    pool2.submit(() -> 0); // throws!
} catch (RejectedExecutionException e) {
    System.out.println("Pool shut down");
}

FutureTask: manuaalinen Future

FutureTask<V> toteuttaa sekä Runnable- että Future<V>-rajapinnan — se on hyödyllinen, kun tarvitsette Futuren ilman ExecutorServiceä:

FutureTask<String> task = new FutureTask<>(() -> "computed");
new Thread(task).start(); // run in any thread
System.out.println(task.get()); // computed

Uudelleenyritysmalli Futuren avulla

Toteuttakaa yksinkertainen uudelleenyrityslogiikka future.get()-metodin ympärille:

Future<String> future = pool.submit(() -> {
    if (Math.random() < 0.7) throw new RuntimeException("transient error");
    return "success";
});

for (int attempt = 0; attempt < 3; attempt++) {
    try {
        System.out.println(future.get());
        break;
    } catch (ExecutionException e) {
        System.out.println("Attempt " + (attempt+1) + " failed");
        if (attempt == 2) throw e;
        future = pool.submit(() -> "retry"); // resubmit
    }
}

Futuren rajoitukset

java.util.concurrent.Futurella on rajoituksia:

  • Ei callback-funktioita — get()-metodi estää säikeen
  • Muunnoksia ei voi ketjuttaa
  • Useita Future-olioita ei voi yhdistää

Käyttäkää seuraavalla kurssilla käsiteltävää CompletableFuture-luokkaa reaktiiviseen, estämättömään koostamiseen.

Pikatarkistus

Tehtävä heittää poikkeuksen. Mikä poikkeus heitetään, kun kutsutte future.get()-metodia?

Kertaus: Future ja virheenkäsittely

Tärkeimmät asiat:

  • future.get() estää säikeen; käyttäkää aina aikakatkaisullista versiota
  • ExecutionException käärii tehtävien poikkeukset; purkakaa kääre getCause()-metodilla
  • cancel(true) lähettää keskeytyksen; isDone/isCancelled ilmaisevat tilan
  • ExecutorCompletionService.take() noutaa Future-oliot valmistumisjärjestyksessä
  • FutureTask toteuttaa sekä Runnable- että Future-rajapinnan
Aloita maksutta

Opi Java tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
104
Oppitunnit
374

Usein kysytyt kysymykset

Onko oppitunti ”Future ja virheenkäsittely” ilmainen?

Kyllä – oppitunnin ”Future ja virheenkäsittely” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Java Academy-kurssin, päivitä CoddyKit PROhon. Java Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Future ja virheenkäsittely”?

Käytä Future.get-metodia aikakatkaisujen kanssa, käsittele ExecutionException ja peruuta käynnissä olevia tehtäviä. Harjoittelet Java Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Java Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Java Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.

Kuinka kauan ”Future ja virheenkäsittely”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Java Academy-oppitunnilla?

Kyllä. Jokainen Java Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. ExecutorService ja säiepoolityypit
  2. Tehtävien lähettäminen: Runnable ja Callable
  3. Future ja virheenkäsittely
  4. ScheduledExecutorService toistuviin tehtäviin
← Takaisin: Java Academy