Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Programmation Concurrente et Asynchrone : Loom ...

José
July 12, 2023

Programmation Concurrente et Asynchrone : Loom en Java 20 et 21

Le projet Loom nous a apporté deux fonctionnalités en préversion dans le JDK 19 : les threads virtuels et la programmation concurrente structurée. Ces deux éléments reçoivent quelques mises à jour dans le JKD 20. Le troisième élément développé dans Loom a également été publié : un nouveau modèle de variables thread-local, appelé scoped values. Cette présentation fait le bilan de ces fonctionnalités, de l'état des patterns qui seront disponibles en programmation concurrente et asynchrone une fois Loom entièrement disponible, et ce que l'on peut attendre pour le JDK 21, prochaine version LTS qui sera publiée en septembre prochain.

José

July 12, 2023
Tweet

More Decks by José

Other Decks in Programming

Transcript

  1. Programmation Concurrente et Asynchrone State of Loom in the JDK

    21 José Paumard Java Developer Advocate Java Platform Group
  2. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 4

    Tune in! Inside Java Newscast JEP Café Dev.java Inside.java Inside Java Podcast Sip of Java Cracking the Java coding interview
  3. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 8

    What is wrong with this code? Concurrency Issues ExecutorService es = ...; Future<V> f1 = es.submit(SomeService::readImage); Future<V> f2 = es.submit(SomeService::readName); Page page = new Page(f1.get(), f2.get());
  4. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 9

    What is wrong with this code? Concurrency Issues Json request = buildContractRequest(id); String contractJson = contractServer.getContract(request); Contract contract = Json.unmarshal(contractJson);
  5. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 10

    What is wrong with this code? Concurrency Issues Contract contract = CompletableFuture.async( () -> buildContractRequest(id)) .thenApply( ContractServer::getContract) .exceptionnaly( exception -> doSomethingWith(exception)) .thenApply( Json::unmarshall).get();
  6. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 11

    What is wrong with this code? Concurrency Issues ExecutorService es = ...; Future<V> f1 = es.submit(SomeService::readImage); Future<V> f2 = es.submit(SomeService::readName); Page page = new Page(f1.get(), f2.get());
  7. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 12

    Processing I/O data: A Thread is idle 99.9999% of the time! How many threads do you need to keep your CPU busy? Concurrency for I/O ms ns ns
  8. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 13

    Loom fixes two things: - blocking a thread is not an issue anymore  Virtual threads - in case of exceptions, no more loose thread  Structured Concurrency Concurrency Issues
  9. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 14

    Using Virtual Threads ExecutorService service = Executors.newVirtualThreadPerTaskExecutor(); Thread virtuaThread = Thread.ofVirtual() .name("Virtual thread") .start(runnable);
  10. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 15

    A Virtual Thread runs on a Carrier Thread How Does it Work? heap Worker 1 start() Virtual thread fork join pool
  11. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 16

    When it blocks, the stack to the heap How Does it Work? heap start() blocked! Virtual thread Worker 1 fork join pool
  12. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 17

    Continuation.yield() copies the stack in the heap How Does it Work? heap start() blocked! yield() Virtual thread Worker 1 fork join pool
  13. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 18

    …until the OS signals that the data is available How Does it Work? heap start() Virtual thread Worker 1 fork join pool
  14. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 19

    … at which point Continuation.run() is called How Does it Work? heap start() Virtual thread run() Worker 1 fork join pool
  15. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 20

    Which mounts the virtual thread on a platform thread again How Does it Work? heap start() Virtual thread Worker 1 fork join pool
  16. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 21

    Which mounts the virtual thread on a platform thread again How Does it Work? heap Virtual thread ? Worker 1 fork join pool Worker 2
  17. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 22

    A virtual thread is a thread - Race conditions, visibility, locking, … are the same Loom: Virtual Threads (21) A Platform thread: - takes ~1ms to start - consumes ~2MB - context switching ~0,1ms A Virtual thread: - takes ~1ms to start - consumes ~200kB Blocking a Virtual Thread is OK!
  18. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 23

    With Loom’s virtual threads: - Launching a thread is cheap (having 1M is fine), no need to pool them anymore - Blocking a thread is cheap, no need to write non- blocking asynchronous code Loom: Virtual Threads (21)
  19. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 24

    The Travel Agency Example Quotation Weather Forecast Travel Page
  20. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 25

    Asynchronous Code var quotationCF = CompletableFuture.supplyAsync(() -> getQuotation()); var weatherCF = CompletableFuture.supplyAsync(() -> getWeather()); CompletableFuture<Page> travelPageCF = quotationCF .exceptionally(t -> { weatherCF.cancel(true); throw new RuntimeException(t); }) .thenCompose( quotation -> weatherCF // .completeOnTimeout(Weather.UNKNOWN, 100, MILLISECONDS) .exceptionally(e -> Weather.UNKNOWN) .thenApply( weather -> buildPage(quotation, weather)));
  21. Quotation Server A 7/12/2023 Copyright © 2021, Oracle and/or its

    affiliates | 26 The Travel Agency Example Quotation Server B Quotation Server C Weather Forecast Server A Weather Forecast Server B Weather Forecast Server C Travel Agency
  22. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 27

    Stuctured Concurrency Based Travel Agency try (var scope = new WeatherScope()) { scope.fork(() -> readWeatherFromA()); scope.fork(() -> readWeatherFromB()); scope.fork(() -> readWeatherFromC()); scope.join(); Weather firstWeather = scope.getFirstWeather(); return firstWeather; }
  23. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 28

    Stuctured Concurrency Based Travel Agency try (var scope = new QuotationScope()) { scope.fork(() -> readQuotationFromA()); scope.fork(() -> readQuotationFromB()); scope.fork(() -> readQuotationFromC()); scope.join(); Quotation bestQuotation = scope.getBestQuotation(); return bestQuotation; }
  24. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 29

    Stuctured Concurrency Based Travel Agency try (var scope = new TravelPageScope()) { scope.fork(() -> getFirstWeather()); scope.fork(() -> getBestQuotation()); scope.join(); TravelPage page = scope.buildTravelPage(); return page; }
  25. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 30

    Stuctured Concurrency Based Travel Agency protected void handleComplete(Subtask<Quotation> task) { switch (task.state()) { case RUNNING -> throw new IllegalStateException("Ooops"); case SUCCESS -> this.quotations.add(task.resultNow()); case FAILED -> this.exceptions.add(task.exceptionNow()); }
  26. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 31

    Stuctured Concurrency Based Travel Agency public Quotation bestQuotation() { return this.quotations.stream() .min(Comparator.comparing(Quotation::quotation)) .orElseThrow(this::exceptions); } public QuotationException exceptions() { QuotationException exception = new QuotationException(); this.exceptions.forEach(exception::addSuppressed); return exception; }
  27. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 32

    Closing PageScope closes the other scopes A Scope can Spawn more Scopes PageScope QuotationScope WeatherScope
  28. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 33

    So many things… - They are mutable - They may be kept alive forever What is Wrong with ThreadLocal?
  29. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 34

    An alternative model for ThreadLocal variables ThreadLocal are supported by virtual threads But you can do better! ScopedValue (prev 21)
  30. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 35

    ScopedValues are non-modifiable They are not bound to a particular thread They are bound to a single method call Welcome to ScopedValue! ScopedValue<String> key = new ScopedValue.newInstance(); ScopedValue.where(key, "KEY_1") .run(() -> doSomethingSmart())); ScopedValue.where(key, "KEY_2") .run(() -> doSomethingSmart()) .run(() -> soSomethingSmarter());
  31. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 36

    ScopedValues are non-modifiable They are not bound to a particular thread They are bound to a single method call Welcome to ScopedValue! ScopedValue<String> key = new ScopedValue.newInstance(); ScopedValue.where(key, "KEY_1") .run(() -> doSomethingSmart())); ScopedValue.where(key, "KEY_2") .run(() -> doSomethingSmart()) .run(() -> soSomethingSmarter());
  32. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 37

    ScopedValues are non-modifiable They are not bound to a particular thread They are bound to a single method call Welcome to ScopedValue! ScopedValue<String> key = new ScopedValue.newInstance(); ScopedValue.where(key, "KEY_1") .run(() -> doSomethingSmart())); ScopedValue.where(key, "KEY_2") .run(() -> doSomethingSmart()) .run(() -> soSomethingSmarter());
  33. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 38

    Welcome to ScopedValue! ScopedValues are non-modifiable They are not bound to a particular thread They are bound to a single method call void doSomethingSmart() { if (key.isBound()) { String value = key.get(); ... } else { throw new IllegalStateException("Key is not bound"); } }
  34. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 39

    Scoped Values are not transmitted to threads reason: you don’t want loose scoped values They are transmitted to StructuredTaskScope reason: you cant have loose scoped values ScopedValue (prev 21)
  35. 7/12/2023 Copyright © 2021, Oracle and/or its affiliates | 43

    Loom is a Work in Progress http://jdk.java.net/loom/