Slide 1

Slide 1 text

Concurrency Made Easy: Structured Concurrency & Scoped Values in Action Sven Woltmann

Slide 2

Slide 2 text

Copyright © 2023, HappyCoders.eu What is Structured Concurrency?

Slide 3

Slide 3 text

Copyright © 2023, HappyCoders.eu What is Unstructured Concurrency?

Slide 4

Slide 4 text

Create invoice Get order Get customer Get invoice template Generate invoice Copyright © 2023, HappyCoders.eu Online Store: Invoice Creation

Slide 5

Slide 5 text

CompletableFuture createInvoiceAsync(int orderId, int customerId, String language) { CompletableFuture orderFuture = orderService.getOrderAsync(orderId); CompletableFuture customerFuture = customerService.getCustomerAsync(customerId); CompletableFuture templateFuture = invoiceTemplateService.getTemplateAsync(language); return CompletableFuture.allOf(orderFuture, customerFuture, templateFuture) .thenCompose( _ -> { Order order = orderFuture.resultNow(); Customer customer = customerFuture.resultNow(); InvoiceTemplate template = templateFuture.resultNow(); Invoice invoice = Invoice.generate(order, customer, template); return CompletableFuture.completedFuture(invoice); }); }

Slide 6

Slide 6 text

CompletableFuture createInvoiceAsync(int orderId, int customerId, String language) { CompletableFuture orderFuture = orderService.getOrderAsync(orderId); CompletableFuture customerFuture = customerService.getCustomerAsync(customerId); CompletableFuture templateFuture = invoiceTemplateService.getTemplateAsync(language); return CompletableFuture.allOf( orderFuture.exceptionally( e -> { customerFuture.cancel(true); templateFuture.cancel(true); if (e instanceof CancellationException) { return null; } else { throw new CompletionException(e); } }), customerFuture.exceptionally( e -> { orderFuture.cancel(true); templateFuture.cancel(true); if (e instanceof CancellationException) { return null; } else { throw new CompletionException(e); } }), templateFuture.exceptionally( e -> { customerFuture.cancel(true); orderFuture.cancel(true); if (e instanceof CancellationException) { return null; } else { throw new CompletionException(e); } })) .thenCompose( _ -> { Order order = orderFuture.resultNow(); Customer customer = customerFuture.resultNow(); InvoiceTemplate template = templateFuture.resultNow(); Invoice invoice = Invoice.generate(order, customer, template); return CompletableFuture.completedFuture(invoice); }); }

Slide 7

Slide 7 text

Copyright © 2023, HappyCoders.eu And with a “VirtualThreadPerTaskExecutor”?

Slide 8

Slide 8 text

Invoice createInvoice(int orderId, int customerId, String language) throws InterruptedException, ExecutionException { try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { Future orderFuture = executor.submit(() -> orderService.getOrder(orderId)); Future customerFuture = executor.submit(() -> customerService.getCustomer(customerId)); Future invoiceTemplateFuture = executor.submit(() -> invoiceTemplateService.getTemplate(language)); Order order = orderFuture.get(); Customer customer = customerFuture.get(); InvoiceTemplate invoiceTemplate = invoiceTemplateFuture.get(); return Invoice.generate(order, customer, invoiceTemplate); } }

Slide 9

Slide 9 text

Invoice createInvoice(int orderId, int customerId, String language) throws Throwable { Future orderFuture; Future customerFuture; Future invoiceTemplateFuture; try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { orderFuture = executor.submit(() -> { try { return orderService.getOrder(orderId); } catch (Exception e) { executor.shutdownNow(); throw e; }}); customerFuture = executor.submit(() -> { try { return customerService.getCustomer(customerId); } catch (Exception e) { executor.shutdownNow(); throw e; }}); invoiceTemplateFuture = executor.submit(() -> { try { return invoiceTemplateService.getTemplate(language); } catch (Exception e) { executor.shutdownNow(); throw e; }}); } if (orderFuture.state() == SUCCESS && customerFuture.state() == SUCCESS && invoiceTemplateFuture.state() == SUCCESS) { Order order = orderFuture.get(); Customer customer = customerFuture.get(); InvoiceTemplate invoiceTemplate = invoiceTemplateFuture.get(); return Invoice.generate(order, customer, invoiceTemplate); } else if (orderFuture.state() == FAILED && !(orderFuture.exceptionNow() instanceof InterruptedException)) { throw orderFuture.exceptionNow(); } else if (customerFuture.state() == FAILED && !(customerFuture.exceptionNow() instanceof InterruptedException)) { throw customerFuture.exceptionNow(); } else if (invoiceTemplateFuture.state() == FAILED) { throw invoiceTemplateFuture.exceptionNow(); } else if (orderFuture.state() == FAILED) { throw orderFuture.exceptionNow(); } else if (customerFuture.state() == FAILED) { throw customerFuture.exceptionNow(); } else { throw new IllegalStateException(); } }

Slide 10

Slide 10 text

API endpoint called Get product data Get availability in warehouse 1 Get availability in warehouse 2 Get availability in warehouse 3 Get availability from seller 1 Get availability from seller 2 Get availability from seller 3 Get delivery time from supplier 1 Get delivery time from supplier 2 Get delivery time from supplier 3 Available? Return product data, delivery time, sellers Yes No Copyright © 2023, HappyCoders.eu

Slide 11

Slide 11 text

Get order Get customer Get invoice template Copyright © 2023, HappyCoders.eu

Slide 12

Slide 12 text

Live Coding: StructuredTaskScope Copyright © 2023, HappyCoders.eu

Slide 13

Slide 13 text

Customer enters address Verify via address verification system A Verify via address verification system B Verify via address verification system C Copyright © 2023, HappyCoders.eu … Online Store: Delivery Address Verification

Slide 14

Slide 14 text

Live Coding Delivery Address Verification with StructuredTaskService Copyright © 2023, HappyCoders.eu

Slide 15

Slide 15 text

Custom StructuredTaskScope Copyright © 2023, HappyCoders.eu

Slide 16

Slide 16 text

API endpoint called Get product data Get delivery time from supplier Available? Return product data, delivery time Get availability in warehouse Yes No Copyright © 2023, HappyCoders.eu Online Store: Supplier Delivery Time Check

Slide 17

Slide 17 text

Get delivery time from supplier A Get delivery time from supplier B Get delivery time from supplier C Available? Return product data, delivery time Yes No Copyright © 2023, HappyCoders.eu API endpoint called Get product data Get availability in warehouse Online Store: Supplier Delivery Time Check

Slide 18

Slide 18 text

Live Coding Supplier Delivery Time Check with StructuredTaskService Copyright © 2023, HappyCoders.eu

Slide 19

Slide 19 text

Nested StructuredTaskScope Copyright © 2023, HappyCoders.eu

Slide 20

Slide 20 text

Copyright © 2023, HappyCoders.eu

Slide 21

Slide 21 text

Live Coding Nested StructuredTaskScope Copyright © 2023, HappyCoders.eu

Slide 22

Slide 22 text

Advantages of Structured Concurrency Copyright © 2023, HappyCoders.eu

Slide 23

Slide 23 text

Unstructured Copyright © 2023, HappyCoders.eu

Slide 24

Slide 24 text

Unstructured Copyright © 2023, HappyCoders.eu Structured

Slide 25

Slide 25 text

Unstructured Copyright © 2023, HappyCoders.eu Structured

Slide 26

Slide 26 text

Copyright © 2023, HappyCoders.eu Advantages of StructuredTaskScope - Clear entry and exit points - Errors in subtasks can be propagated to parent scope (or ignored) - All threads are terminated at the end of the scope ● Language construct for Structured Concurrency ● Easy to write, read, and maintain ● Policies: ShutdownOnSuccess, ShutdownOnFailure, custom policies - Suceeded/failed subtasks can cancel others

Slide 27

Slide 27 text

Copyright © 2023, HappyCoders.eu Things to Consider ● Make sure your tasks are interruptible ● Make custom policies thread-safe

Slide 28

Slide 28 text

Scoped Values Copyright © 2023, HappyCoders.eu

Slide 29

Slide 29 text

What are Scoped Values? Copyright © 2023, HappyCoders.eu

Slide 30

Slide 30 text

Copyright © 2023, HappyCoders.eu Disadvantages of ThreadLocal ● In memory until thread dies or ThreadLocal.remove() is called ● Mutable ● Stored in a HashMap → No clear scope, memory leaks → Complex code → High memory footprint ● Copied to child threads when using InheritableThreadLocal → Even higher memory footprint

Slide 31

Slide 31 text

Live Coding: Scoped Values Copyright © 2023, HappyCoders.eu

Slide 32

Slide 32 text

Scoped Values Copyright © 2023, HappyCoders.eu = implicit method parameters

Slide 33

Slide 33 text

Scoped Values and Structured Concurrency Copyright © 2023, HappyCoders.eu

Slide 34

Slide 34 text

Copyright © 2023, HappyCoders.eu ScopedValue.where(USER, user).run(...);

Slide 35

Slide 35 text

Copyright © 2023, HappyCoders.eu getSupplierDeliveryTimes(...) getSupplierDeliveryTime(...) getSupplierDeliveryTime(...) getSupplierDeliveryTime(...)

Slide 36

Slide 36 text

Copyright © 2023, HappyCoders.eu user user user user user user user user user user user user ScopedValue.where(USER, user).run(...); ScopedValue .where(USER, null).run(...);

Slide 37

Slide 37 text

Copyright © 2023, HappyCoders.eu ScopedValue API ScopedValue.where(USER, user).run(runnable); ScopedValue.where(USER, user).call(callable); ScopedValue.where(USER, user).get(supplier); ScopedValue.where(USER, user) .where(SESSION, session) .run(runnable); ScopedValue.runWhere(USER, user, runnable); ScopedValue.callWhere(USER, user, callable); ScopedValue.getWhere(USER, user, supplier);

Slide 38

Slide 38 text

Copyright © 2023, HappyCoders.eu ScopedValue API User user = USER.get(); User user = USER.orElse(null); User user = USER.orElseThrow(UserNotLoggedInException::new); if (USER.isBound()) { User user = USER.get(); ... }

Slide 39

Slide 39 text

Copyright © 2023, HappyCoders.eu ● No need to add a parameter to every method - Methods that don’t use it - Methods that must not see it Advantages of ScopedValues (over method parameters) - Methods you cannot change

Slide 40

Slide 40 text

Copyright © 2023, HappyCoders.eu ● Only valid in the scope of the Runnable / Callable / Supplier ● Immutable (only “rebindable”) ● Stored in a Carrier → Clear scope, no memory leaks → Simpler code → Lower memory footprint ● Not copied to child threads upfront Advantages of ScopedValues (over ThreadLocals) ● But cached (only 16) ● -Djava.lang.ScopedValue.cacheSize=2 / 4 / 8 / 16 (default) ● -Djdk.preserveScopedValueCache=false (default: true)

Slide 41

Slide 41 text

Copyright © 2023, HappyCoders.eu The End

Slide 42

Slide 42 text

Copyright © 2023, HappyCoders.eu GitHub repositories: https://github.com/SvenWoltmann/structured-concurrency https://github.com/SvenWoltmann/scoped-values JEP 453: Structured Concurrency (Preview): https://openjdk.org/jeps/453 Scoped Values in Java: https://www.happycoders.eu/java/scoped-values/ https://twitter.com/svenwoltmann https://youtube.com/c/happycoders Links https://www.happycoders.eu/ https://github.com/SvenWoltmann https://linkedin.com/in/sven-woltmann/ JEP 446: Scoped Values (Preview): https://openjdk.org/jeps/446 Structured Concurrency in Java: https://www.happycoders.eu/java/structured-concurrency