├── img ├── big-o-cheat-sheet-pdf-en-transp_936.v2.png ├── Java_Versions_PDF_Cheat_Sheet_Mockup_936.v2.png └── mastering-data-structures-product-mockup-cropped-1600.v2.png ├── src └── main │ └── java │ └── eu │ └── happycoders │ └── structuredconcurrency │ ├── demo1_invoice │ ├── model │ │ ├── Order.java │ │ ├── Customer.java │ │ ├── InvoiceTemplate.java │ │ └── Invoice.java │ ├── service │ │ ├── Properties.java │ │ ├── OrderService.java │ │ ├── CustomerService.java │ │ └── InvoiceTemplateService.java │ ├── InvoiceGenerator1_Sequential.java │ ├── InvoiceGenerator2_CompletableFuture.java │ ├── InvoiceGenerator5_StructuredTaskScope.java │ ├── InvoiceGenerator3_ThreadPool.java │ ├── InvoiceGenerator4_NewVirtualThreadPerTask.java │ ├── InvoiceGenerator2b_CompletableFutureCancelling.java │ ├── InvoiceGenerator4b_NewVirtualThreadPerTaskCancelling_NoHints.java │ └── InvoiceGenerator4b_NewVirtualThreadPerTaskCancelling.java │ ├── demo2_address │ ├── model │ │ ├── AddressVerificationResponse.java │ │ └── Address.java │ ├── AddressVerification1_Sequential.java │ ├── AddressVerification2_AnySuccessfulResult.java │ └── service │ │ └── AddressVerificationService.java │ ├── demo3_suppliers │ ├── SupplierDeliveryTimeCheckException.java │ ├── model │ │ └── SupplierDeliveryTime.java │ ├── service │ │ ├── SupplierDeliveryTimeService.java │ │ └── SupplierDeliveryTimeServiceUsingScopedValue.java │ ├── BestResultJoiner.java │ ├── SupplierDeliveryTimeCheck2_StructuredTaskScope.java │ ├── SupplierDeliveryTimeCheck1_Sequential.java │ ├── SupplierDeliveryTimeCheck3_NestedStructuredTaskScope.java │ └── SupplierDeliveryTimeCheck4_NestedStructuredTaskScopeUsingScopedValue.java │ └── util │ └── SimpleLogger.java ├── .gitignore ├── pom.xml └── README.md /img/big-o-cheat-sheet-pdf-en-transp_936.v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SvenWoltmann/structured-concurrency/HEAD/img/big-o-cheat-sheet-pdf-en-transp_936.v2.png -------------------------------------------------------------------------------- /img/Java_Versions_PDF_Cheat_Sheet_Mockup_936.v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SvenWoltmann/structured-concurrency/HEAD/img/Java_Versions_PDF_Cheat_Sheet_Mockup_936.v2.png -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/model/Order.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice.model; 2 | 3 | public record Order() {} 4 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/model/Customer.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice.model; 2 | 3 | public record Customer() {} 4 | -------------------------------------------------------------------------------- /img/mastering-data-structures-product-mockup-cropped-1600.v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SvenWoltmann/structured-concurrency/HEAD/img/mastering-data-structures-product-mockup-cropped-1600.v2.png -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/model/InvoiceTemplate.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice.model; 2 | 3 | public record InvoiceTemplate() {} 4 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo2_address/model/AddressVerificationResponse.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo2_address.model; 2 | 3 | public record AddressVerificationResponse(String value) {} 4 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo3_suppliers/SupplierDeliveryTimeCheckException.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo3_suppliers; 2 | 3 | public class SupplierDeliveryTimeCheckException extends Exception {} 4 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo3_suppliers/model/SupplierDeliveryTime.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo3_suppliers.model; 2 | 3 | public record SupplierDeliveryTime(String supplier, int deliveryTimeHours) {} 4 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/model/Invoice.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice.model; 2 | 3 | public record Invoice() { 4 | public static Invoice generate(Order order, Customer customer, InvoiceTemplate template) { 5 | return new Invoice(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/service/Properties.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice.service; 2 | 3 | public class Properties { 4 | 5 | // In presentation mode, the outcome of the services is predictable 6 | public static final boolean PRESENTATION_MODE = false; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo2_address/model/Address.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo2_address.model; 2 | 3 | public record Address( 4 | String addressLine1, 5 | String addressLine2, 6 | String city, 7 | String state, 8 | String postalCode, 9 | String countryCode) {} 10 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/util/SimpleLogger.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.util; 2 | 3 | public final class SimpleLogger { 4 | 5 | private SimpleLogger() {} 6 | 7 | public static void log(String description) { 8 | System.out.printf("[%s] %s%n", Thread.currentThread(), description); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | # Eclipse 26 | .classpath 27 | .project 28 | .settings/ 29 | 30 | # Intellij 31 | .idea/ 32 | *.iml 33 | *.iws 34 | 35 | # Mac 36 | .DS_Store 37 | 38 | # Maven 39 | log/ 40 | target/ -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice.service; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Order; 6 | import java.util.concurrent.CompletableFuture; 7 | import java.util.concurrent.ThreadLocalRandom; 8 | 9 | public class OrderService { 10 | 11 | public Order getOrder(int orderId) throws InterruptedException { 12 | log("Loading order"); 13 | try { 14 | long minSleepTime = Properties.PRESENTATION_MODE ? 900 : 500; 15 | long maxSleepTime = 1000; 16 | Thread.sleep(ThreadLocalRandom.current().nextLong(minSleepTime, maxSleepTime)); 17 | } catch (InterruptedException e) { 18 | log("Order loading was interrupted"); 19 | throw e; 20 | } 21 | 22 | if (!Properties.PRESENTATION_MODE) { 23 | if (ThreadLocalRandom.current().nextDouble() < 0.2) { 24 | log("Error loading order"); 25 | throw new RuntimeException("Error loading order"); 26 | } 27 | } 28 | 29 | log("Finished loading order"); 30 | return new Order(); 31 | } 32 | 33 | public CompletableFuture getOrderAsync(int orderId) { 34 | return CompletableFuture.supplyAsync( 35 | () -> { 36 | try { 37 | return getOrder(orderId); 38 | } catch (InterruptedException e) { 39 | throw new RuntimeException(e); 40 | } 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/service/CustomerService.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice.service; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Customer; 6 | import java.util.concurrent.CompletableFuture; 7 | import java.util.concurrent.ThreadLocalRandom; 8 | 9 | public class CustomerService { 10 | 11 | public Customer getCustomer(int customerId) throws InterruptedException { 12 | log("Loading customer"); 13 | try { 14 | long minSleepTime = Properties.PRESENTATION_MODE ? 300 : 500; 15 | long maxSleepTime = Properties.PRESENTATION_MODE ? 400 : 1000; 16 | Thread.sleep(ThreadLocalRandom.current().nextLong(minSleepTime, maxSleepTime)); 17 | } catch (InterruptedException e) { 18 | log("Customer loading was interrupted"); 19 | throw e; 20 | } 21 | 22 | if (!Properties.PRESENTATION_MODE) { 23 | if (ThreadLocalRandom.current().nextDouble() < 0.2) { 24 | log("Error loading customer"); 25 | throw new RuntimeException("Error loading customer"); 26 | } 27 | } 28 | 29 | log("Finished loading customer"); 30 | return new Customer(); 31 | } 32 | 33 | public CompletableFuture getCustomerAsync(int customerId) { 34 | return CompletableFuture.supplyAsync( 35 | () -> { 36 | try { 37 | return getCustomer(customerId); 38 | } catch (InterruptedException e) { 39 | throw new RuntimeException(e); 40 | } 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | eu.happycoders 8 | structured-concurrency-demo 9 | 1.1.0-SNAPSHOT 10 | 11 | 12 | UTF-8 13 | 14 | 15 | 16 | 17 | 18 | org.apache.maven.plugins 19 | maven-compiler-plugin 20 | 3.14.0 21 | 22 | 25 23 | 24 | true 25 | 26 | 27 | 28 | 29 | com.diffplug.spotless 30 | spotless-maven-plugin 31 | 2.44.5 32 | 33 | 34 | 35 | 36 | 1.27.0 37 | 38 | 39 | UNIX 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo3_suppliers/service/SupplierDeliveryTimeService.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo3_suppliers.service; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo3_suppliers.model.SupplierDeliveryTime; 6 | import java.util.concurrent.ThreadLocalRandom; 7 | 8 | public class SupplierDeliveryTimeService { 9 | 10 | private final boolean failAll; 11 | 12 | public SupplierDeliveryTimeService(boolean failAll) { 13 | this.failAll = failAll; 14 | } 15 | 16 | public SupplierDeliveryTime getDeliveryTime(String productId, String supplier) 17 | throws InterruptedException { 18 | log("Retrieving delivery time from supplier " + supplier); 19 | 20 | try { 21 | Thread.sleep(ThreadLocalRandom.current().nextLong(250, 1000)); 22 | } catch (InterruptedException e) { 23 | log("Retrieving delivery time from supplier " + supplier + " interrupted"); 24 | throw e; 25 | } 26 | 27 | // 40% failure probability --> 2 out of 5 requests should fail 28 | if (failAll || ThreadLocalRandom.current().nextDouble() < 0.4) { 29 | log("Error retrieving delivery time from supplier " + supplier); 30 | throw new RuntimeException("Error retrieving delivery time from supplier " + supplier); 31 | } 32 | 33 | int deliveryTimeHours = ThreadLocalRandom.current().nextInt(1, 7 * 24); 34 | 35 | log( 36 | "Finished retrieving delivery time from supplier %s: %d hours" 37 | .formatted(supplier, deliveryTimeHours)); 38 | 39 | return new SupplierDeliveryTime(supplier, deliveryTimeHours); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/service/InvoiceTemplateService.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice.service; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.InvoiceTemplate; 6 | import java.util.concurrent.CompletableFuture; 7 | import java.util.concurrent.ThreadLocalRandom; 8 | 9 | public class InvoiceTemplateService { 10 | 11 | public InvoiceTemplate getTemplate(String language) throws InterruptedException { 12 | log("Loading template"); 13 | try { 14 | long minSleepTime = Properties.PRESENTATION_MODE ? 600 : 500; 15 | long maxSleepTime = Properties.PRESENTATION_MODE ? 700 : 1000; 16 | Thread.sleep(ThreadLocalRandom.current().nextLong(minSleepTime, maxSleepTime)); 17 | } catch (InterruptedException e) { 18 | log("Template loading was interrupted"); 19 | throw e; 20 | } 21 | 22 | double errorProbability = Properties.PRESENTATION_MODE ? 0.5 : 0.2; 23 | if (ThreadLocalRandom.current().nextDouble() < errorProbability) { 24 | log("Error loading template"); 25 | throw new RuntimeException("Error loading template"); 26 | } 27 | 28 | log("Finished loading template"); 29 | return new InvoiceTemplate(); 30 | } 31 | 32 | public CompletableFuture getTemplateAsync(String language) { 33 | return CompletableFuture.supplyAsync( 34 | () -> { 35 | try { 36 | return getTemplate(language); 37 | } catch (InterruptedException e) { 38 | throw new RuntimeException(e); 39 | } 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo3_suppliers/BestResultJoiner.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo3_suppliers; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.Comparator; 6 | import java.util.List; 7 | import java.util.concurrent.StructuredTaskScope.Joiner; 8 | import java.util.concurrent.StructuredTaskScope.Subtask; 9 | 10 | public class BestResultJoiner implements Joiner { 11 | 12 | private final Comparator comparator; 13 | 14 | private T bestResult; 15 | private final List exceptions = Collections.synchronizedList(new ArrayList<>()); 16 | 17 | public BestResultJoiner(Comparator comparator) { 18 | this.comparator = comparator; 19 | } 20 | 21 | @Override 22 | public boolean onComplete(Subtask subtask) { 23 | switch (subtask.state()) { 24 | case UNAVAILABLE -> { 25 | // Ignore 26 | } 27 | case SUCCESS -> { 28 | T result = subtask.get(); 29 | synchronized (this) { 30 | if (bestResult == null || comparator.compare(result, bestResult) > 0) { 31 | bestResult = result; 32 | } 33 | } 34 | } 35 | case FAILED -> exceptions.add(subtask.exception()); 36 | } 37 | 38 | return false; // Don't cancel the scope 39 | } 40 | 41 | @Override 42 | public T result() throws SupplierDeliveryTimeCheckException { 43 | if (bestResult != null) { 44 | return bestResult; 45 | } else { 46 | SupplierDeliveryTimeCheckException exception = new SupplierDeliveryTimeCheckException(); 47 | exceptions.forEach(exception::addSuppressed); 48 | throw exception; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo3_suppliers/SupplierDeliveryTimeCheck2_StructuredTaskScope.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo3_suppliers; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo3_suppliers.model.SupplierDeliveryTime; 6 | import eu.happycoders.structuredconcurrency.demo3_suppliers.service.SupplierDeliveryTimeService; 7 | import java.util.Comparator; 8 | import java.util.List; 9 | import java.util.concurrent.StructuredTaskScope; 10 | 11 | public class SupplierDeliveryTimeCheck2_StructuredTaskScope { 12 | 13 | private static final boolean FAIL_ALL = false; 14 | 15 | static void main() throws InterruptedException { 16 | SupplierDeliveryTimeCheck2_StructuredTaskScope supplierDeliveryTimeCheck = 17 | new SupplierDeliveryTimeCheck2_StructuredTaskScope( 18 | new SupplierDeliveryTimeService(FAIL_ALL)); 19 | SupplierDeliveryTime response = 20 | supplierDeliveryTimeCheck.getSupplierDeliveryTime( 21 | "B004V9OA84", List.of("A", "B", "C", "D", "E")); 22 | log("Response: " + response); 23 | } 24 | 25 | private final SupplierDeliveryTimeService service; 26 | 27 | public SupplierDeliveryTimeCheck2_StructuredTaskScope(SupplierDeliveryTimeService service) { 28 | this.service = service; 29 | } 30 | 31 | SupplierDeliveryTime getSupplierDeliveryTime(String productId, List supplierIds) 32 | throws InterruptedException { 33 | try (var scope = 34 | StructuredTaskScope.open( 35 | new BestResultJoiner<>( 36 | Comparator.comparing(SupplierDeliveryTime::deliveryTimeHours).reversed()))) { 37 | for (String supplierId : supplierIds) { 38 | scope.fork(() -> service.getDeliveryTime(productId, supplierId)); 39 | } 40 | 41 | return scope.join(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo2_address/AddressVerification1_Sequential.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo2_address; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo2_address.model.Address; 6 | import eu.happycoders.structuredconcurrency.demo2_address.model.AddressVerificationResponse; 7 | import eu.happycoders.structuredconcurrency.demo2_address.service.AddressVerificationService; 8 | 9 | public class AddressVerification1_Sequential { 10 | 11 | static void main() throws InterruptedException { 12 | AddressVerification1_Sequential addressVerification = 13 | new AddressVerification1_Sequential(new AddressVerificationService()); 14 | AddressVerificationResponse response = 15 | addressVerification.verifyAddress( 16 | new Address("1600 Pennsylvania Avenue, N.W.", null, "Washington", "DC", "20500", "US")); 17 | log("Response: " + response); 18 | } 19 | 20 | private final AddressVerificationService verificationService; 21 | 22 | public AddressVerification1_Sequential(AddressVerificationService verificationService) { 23 | this.verificationService = verificationService; 24 | } 25 | 26 | AddressVerificationResponse verifyAddress(Address address) throws InterruptedException { 27 | try { 28 | return verificationService.verifyViaServiceA(address); 29 | } catch (Exception e) { 30 | log("Verification via service A failed: " + e.getMessage()); 31 | } 32 | 33 | try { 34 | return verificationService.verifyViaServiceB(address); 35 | } catch (Exception e) { 36 | log("Verification via service B failed: " + e.getMessage()); 37 | } 38 | 39 | try { 40 | return verificationService.verifyViaServiceC(address); 41 | } catch (Exception e) { 42 | log("Verification via service C failed: " + e.getMessage()); 43 | throw e; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo3_suppliers/service/SupplierDeliveryTimeServiceUsingScopedValue.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo3_suppliers.service; 2 | 3 | import static eu.happycoders.structuredconcurrency.demo3_suppliers.SupplierDeliveryTimeCheck4_NestedStructuredTaskScopeUsingScopedValue.API_KEY; 4 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 5 | 6 | import eu.happycoders.structuredconcurrency.demo3_suppliers.model.SupplierDeliveryTime; 7 | import java.util.concurrent.ThreadLocalRandom; 8 | 9 | public class SupplierDeliveryTimeServiceUsingScopedValue { 10 | 11 | private final boolean failAll; 12 | 13 | public SupplierDeliveryTimeServiceUsingScopedValue(boolean failAll) { 14 | this.failAll = failAll; 15 | } 16 | 17 | public SupplierDeliveryTime getDeliveryTime(String productId, String supplier) 18 | throws InterruptedException { 19 | String apiKey = API_KEY.get(); 20 | log( 21 | "Retrieving delivery time from supplier %s (using API key %s)..." 22 | .formatted(supplier, apiKey)); 23 | 24 | try { 25 | Thread.sleep(ThreadLocalRandom.current().nextLong(250, 1000)); 26 | } catch (InterruptedException e) { 27 | log("Retrieving delivery time from supplier " + supplier + " interrupted"); 28 | throw e; 29 | } 30 | 31 | // 40% failure probability --> 2 out of 5 requests should fail 32 | if (failAll || ThreadLocalRandom.current().nextDouble() < 0.4) { 33 | log("Error retrieving delivery time from supplier " + supplier); 34 | throw new RuntimeException("Error retrieving delivery time from supplier " + supplier); 35 | } 36 | 37 | int deliveryTimeHours = ThreadLocalRandom.current().nextInt(1, 7 * 24); 38 | 39 | log( 40 | "Finished retrieving delivery time from supplier %s: %d hours" 41 | .formatted(supplier, deliveryTimeHours)); 42 | 43 | return new SupplierDeliveryTime(supplier, deliveryTimeHours); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo2_address/AddressVerification2_AnySuccessfulResult.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo2_address; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo2_address.model.Address; 6 | import eu.happycoders.structuredconcurrency.demo2_address.model.AddressVerificationResponse; 7 | import eu.happycoders.structuredconcurrency.demo2_address.service.AddressVerificationService; 8 | import java.util.concurrent.StructuredTaskScope; 9 | import java.util.concurrent.StructuredTaskScope.Joiner; 10 | 11 | public class AddressVerification2_AnySuccessfulResult { 12 | 13 | static void main() throws InterruptedException { 14 | AddressVerification2_AnySuccessfulResult addressVerification = 15 | new AddressVerification2_AnySuccessfulResult(new AddressVerificationService()); 16 | AddressVerificationResponse response = 17 | addressVerification.verifyAddress( 18 | new Address("1600 Pennsylvania Avenue, N.W.", null, "Washington", "DC", "20500", "US")); 19 | log("Response: " + response); 20 | } 21 | 22 | private final AddressVerificationService verificationService; 23 | 24 | public AddressVerification2_AnySuccessfulResult(AddressVerificationService verificationService) { 25 | this.verificationService = verificationService; 26 | } 27 | 28 | AddressVerificationResponse verifyAddress(Address address) throws InterruptedException { 29 | try (var scope = 30 | StructuredTaskScope.open( 31 | Joiner.anySuccessfulResultOrThrow())) { 32 | log("Forking tasks"); 33 | 34 | scope.fork(() -> verificationService.verifyViaServiceA(address)); 35 | scope.fork(() -> verificationService.verifyViaServiceB(address)); 36 | scope.fork(() -> verificationService.verifyViaServiceC(address)); 37 | 38 | log("Waiting for one task to finish"); 39 | 40 | return scope.join(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/InvoiceGenerator1_Sequential.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice; 2 | 3 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Customer; 4 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Invoice; 5 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.InvoiceTemplate; 6 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Order; 7 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.CustomerService; 8 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.InvoiceTemplateService; 9 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.OrderService; 10 | import java.util.concurrent.ExecutionException; 11 | 12 | public class InvoiceGenerator1_Sequential { 13 | 14 | static void main() throws InterruptedException, ExecutionException { 15 | InvoiceGenerator1_Sequential invoiceGenerator = 16 | new InvoiceGenerator1_Sequential( 17 | new OrderService(), new CustomerService(), new InvoiceTemplateService()); 18 | invoiceGenerator.createInvoice(10012, 61157, "en"); 19 | } 20 | 21 | private final OrderService orderService; 22 | private final CustomerService customerService; 23 | private final InvoiceTemplateService invoiceTemplateService; 24 | 25 | public InvoiceGenerator1_Sequential( 26 | OrderService orderService, 27 | CustomerService customerService, 28 | InvoiceTemplateService invoiceTemplateService) { 29 | this.orderService = orderService; 30 | this.customerService = customerService; 31 | this.invoiceTemplateService = invoiceTemplateService; 32 | } 33 | 34 | Invoice createInvoice(int orderId, int customerId, String language) 35 | throws InterruptedException, ExecutionException { 36 | Order order = orderService.getOrder(orderId); 37 | 38 | Customer customer = customerService.getCustomer(customerId); 39 | 40 | InvoiceTemplate template = invoiceTemplateService.getTemplate(language); 41 | 42 | return Invoice.generate(order, customer, template); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo3_suppliers/SupplierDeliveryTimeCheck1_Sequential.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo3_suppliers; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo3_suppliers.model.SupplierDeliveryTime; 6 | import eu.happycoders.structuredconcurrency.demo3_suppliers.service.SupplierDeliveryTimeService; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class SupplierDeliveryTimeCheck1_Sequential { 11 | 12 | private static final boolean FAIL_ALL = false; 13 | 14 | static void main() throws SupplierDeliveryTimeCheckException { 15 | SupplierDeliveryTimeCheck1_Sequential supplierDeliveryTimeCheck = 16 | new SupplierDeliveryTimeCheck1_Sequential(new SupplierDeliveryTimeService(FAIL_ALL)); 17 | SupplierDeliveryTime response = 18 | supplierDeliveryTimeCheck.getSupplierDeliveryTime( 19 | "B004V9OA84", List.of("A", "B", "C", "D", "E")); 20 | log("Response: " + response); 21 | } 22 | 23 | private final SupplierDeliveryTimeService service; 24 | 25 | public SupplierDeliveryTimeCheck1_Sequential(SupplierDeliveryTimeService service) { 26 | this.service = service; 27 | } 28 | 29 | SupplierDeliveryTime getSupplierDeliveryTime(String productId, List supplierIds) 30 | throws SupplierDeliveryTimeCheckException { 31 | List exceptions = new ArrayList<>(); 32 | SupplierDeliveryTime fastestDeliveryTime = null; 33 | 34 | for (String supplierId : supplierIds) { 35 | try { 36 | SupplierDeliveryTime deliveryTime = service.getDeliveryTime(productId, supplierId); 37 | if (fastestDeliveryTime == null 38 | || deliveryTime.deliveryTimeHours() < fastestDeliveryTime.deliveryTimeHours()) { 39 | fastestDeliveryTime = deliveryTime; 40 | } 41 | } catch (Throwable e) { 42 | exceptions.add(e); 43 | } 44 | } 45 | 46 | log("--> fastestDeliveryTime = " + fastestDeliveryTime); 47 | log("--> exceptions = " + exceptions); 48 | 49 | if (fastestDeliveryTime != null) { 50 | return fastestDeliveryTime; 51 | } else { 52 | SupplierDeliveryTimeCheckException exception = new SupplierDeliveryTimeCheckException(); 53 | exceptions.forEach(exception::addSuppressed); 54 | throw exception; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo3_suppliers/SupplierDeliveryTimeCheck3_NestedStructuredTaskScope.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo3_suppliers; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo3_suppliers.model.SupplierDeliveryTime; 6 | import eu.happycoders.structuredconcurrency.demo3_suppliers.service.SupplierDeliveryTimeService; 7 | import java.util.Comparator; 8 | import java.util.List; 9 | import java.util.concurrent.StructuredTaskScope; 10 | import java.util.concurrent.StructuredTaskScope.Joiner; 11 | import java.util.concurrent.StructuredTaskScope.Subtask; 12 | 13 | public class SupplierDeliveryTimeCheck3_NestedStructuredTaskScope { 14 | 15 | private static final boolean FAIL_ALL = false; 16 | 17 | static void main() throws InterruptedException { 18 | SupplierDeliveryTimeCheck3_NestedStructuredTaskScope supplierDeliveryTimeCheck = 19 | new SupplierDeliveryTimeCheck3_NestedStructuredTaskScope( 20 | new SupplierDeliveryTimeService(FAIL_ALL)); 21 | List responses = 22 | supplierDeliveryTimeCheck.getSupplierDeliveryTimes( 23 | List.of("B004V9OA84", "0201310090", "0134685997"), List.of("A", "B", "C", "D", "E")); 24 | log("Responses: " + responses); 25 | } 26 | 27 | private final SupplierDeliveryTimeService service; 28 | 29 | public SupplierDeliveryTimeCheck3_NestedStructuredTaskScope(SupplierDeliveryTimeService service) { 30 | this.service = service; 31 | } 32 | 33 | List getSupplierDeliveryTimes( 34 | List productIds, List supplierIds) throws InterruptedException { 35 | try (var scope = 36 | StructuredTaskScope.open(Joiner.allSuccessfulOrThrow())) { 37 | productIds.forEach( 38 | productId -> scope.fork(() -> getSupplierDeliveryTime(productId, supplierIds))); 39 | 40 | return scope.join().map(Subtask::get).toList(); 41 | } 42 | } 43 | 44 | SupplierDeliveryTime getSupplierDeliveryTime(String productId, List supplierIds) 45 | throws InterruptedException { 46 | try (var scope = 47 | StructuredTaskScope.open( 48 | new BestResultJoiner<>( 49 | Comparator.comparing(SupplierDeliveryTime::deliveryTimeHours).reversed()))) { 50 | for (String supplierId : supplierIds) { 51 | scope.fork(() -> service.getDeliveryTime(productId, supplierId)); 52 | } 53 | 54 | return scope.join(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/InvoiceGenerator2_CompletableFuture.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice; 2 | 3 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Customer; 4 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Invoice; 5 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.InvoiceTemplate; 6 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Order; 7 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.CustomerService; 8 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.InvoiceTemplateService; 9 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.OrderService; 10 | import java.util.concurrent.CompletableFuture; 11 | 12 | public class InvoiceGenerator2_CompletableFuture { 13 | 14 | static void main() { 15 | InvoiceGenerator2_CompletableFuture invoiceGenerator = 16 | new InvoiceGenerator2_CompletableFuture( 17 | new OrderService(), new CustomerService(), new InvoiceTemplateService()); 18 | invoiceGenerator.createInvoiceAsync(10012, 61157, "en").join(); 19 | } 20 | 21 | private final OrderService orderService; 22 | private final CustomerService customerService; 23 | private final InvoiceTemplateService invoiceTemplateService; 24 | 25 | public InvoiceGenerator2_CompletableFuture( 26 | OrderService orderService, 27 | CustomerService customerService, 28 | InvoiceTemplateService invoiceTemplateService) { 29 | this.orderService = orderService; 30 | this.customerService = customerService; 31 | this.invoiceTemplateService = invoiceTemplateService; 32 | } 33 | 34 | CompletableFuture createInvoiceAsync(int orderId, int customerId, String language) { 35 | CompletableFuture orderFuture = orderService.getOrderAsync(orderId); 36 | CompletableFuture customerFuture = customerService.getCustomerAsync(customerId); 37 | CompletableFuture templateFuture = 38 | invoiceTemplateService.getTemplateAsync(language); 39 | 40 | return CompletableFuture.allOf(orderFuture, customerFuture, templateFuture) 41 | .thenCompose( 42 | _ -> { 43 | Order order = orderFuture.resultNow(); 44 | Customer customer = customerFuture.resultNow(); 45 | InvoiceTemplate template = templateFuture.resultNow(); 46 | Invoice invoice = Invoice.generate(order, customer, template); 47 | 48 | return CompletableFuture.completedFuture(invoice); 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/InvoiceGenerator5_StructuredTaskScope.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Customer; 6 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Invoice; 7 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.InvoiceTemplate; 8 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Order; 9 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.CustomerService; 10 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.InvoiceTemplateService; 11 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.OrderService; 12 | import java.util.concurrent.StructuredTaskScope; 13 | import java.util.concurrent.StructuredTaskScope.Subtask; 14 | 15 | public class InvoiceGenerator5_StructuredTaskScope { 16 | 17 | static void main() throws InterruptedException { 18 | InvoiceGenerator5_StructuredTaskScope invoiceGenerator = 19 | new InvoiceGenerator5_StructuredTaskScope( 20 | new OrderService(), new CustomerService(), new InvoiceTemplateService()); 21 | invoiceGenerator.createInvoice(10012, 61157, "en"); 22 | } 23 | 24 | private final OrderService orderService; 25 | private final CustomerService customerService; 26 | private final InvoiceTemplateService invoiceTemplateService; 27 | 28 | public InvoiceGenerator5_StructuredTaskScope( 29 | OrderService orderService, 30 | CustomerService customerService, 31 | InvoiceTemplateService invoiceTemplateService) { 32 | this.orderService = orderService; 33 | this.customerService = customerService; 34 | this.invoiceTemplateService = invoiceTemplateService; 35 | } 36 | 37 | Invoice createInvoice(int orderId, int customerId, String language) throws InterruptedException { 38 | try (var scope = StructuredTaskScope.open()) { 39 | log("Forking tasks"); 40 | 41 | Subtask orderSubtask = scope.fork(() -> orderService.getOrder(orderId)); 42 | Subtask customerSubtask = scope.fork(() -> customerService.getCustomer(customerId)); 43 | Subtask invoiceTemplateSubtask = 44 | scope.fork(() -> invoiceTemplateService.getTemplate(language)); 45 | 46 | log("Waiting for all tasks to finish"); 47 | scope.join(); 48 | 49 | log("Retrieving results"); 50 | Order order = orderSubtask.get(); 51 | Customer customer = customerSubtask.get(); 52 | InvoiceTemplate template = invoiceTemplateSubtask.get(); 53 | 54 | log("Generating and returning invoice"); 55 | return Invoice.generate(order, customer, template); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo2_address/service/AddressVerificationService.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo2_address.service; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.Properties; 6 | import eu.happycoders.structuredconcurrency.demo2_address.model.Address; 7 | import eu.happycoders.structuredconcurrency.demo2_address.model.AddressVerificationResponse; 8 | import java.util.concurrent.ThreadLocalRandom; 9 | 10 | public class AddressVerificationService { 11 | 12 | public AddressVerificationResponse verifyViaServiceA(Address address) 13 | throws InterruptedException { 14 | return verifyViaService(address, "A"); 15 | } 16 | 17 | public AddressVerificationResponse verifyViaServiceB(Address address) 18 | throws InterruptedException { 19 | return verifyViaService(address, "B"); 20 | } 21 | 22 | public AddressVerificationResponse verifyViaServiceC(Address address) 23 | throws InterruptedException { 24 | return verifyViaService(address, "C"); 25 | } 26 | 27 | private AddressVerificationResponse verifyViaService(Address address, String service) 28 | throws InterruptedException { 29 | log("Verifying address via service " + service); 30 | try { 31 | // In presentation mode, we want the services to return in a predictable order 32 | long minSleepTime = 33 | Properties.PRESENTATION_MODE 34 | ? (switch (service) { 35 | case "A" -> 900; 36 | case "B" -> 300; 37 | case "C" -> 600; 38 | default -> throw new IllegalStateException(); 39 | }) 40 | : 500; 41 | long maxSleepTime = Properties.PRESENTATION_MODE ? minSleepTime + 100 : 1000; 42 | Thread.sleep(ThreadLocalRandom.current().nextLong(minSleepTime, maxSleepTime)); 43 | } catch (InterruptedException e) { 44 | log("Verifying address via service " + service + " was interrupted"); 45 | throw e; 46 | } 47 | 48 | // In presentation mode, we either want that 49 | // a) all of them fail, so we get an error 50 | // b) none fails, so we get the first's (B) response, and we cancel the two others 51 | // c) the first one (B) fails, so we get the second's (C) response, and we cancel the third (A) 52 | double errorProbability = 53 | Properties.PRESENTATION_MODE ? (service.equals("B") ? 0.5 : 1.0) : 0.75; 54 | if (ThreadLocalRandom.current().nextDouble() < errorProbability) { 55 | log("Error loading address via service " + service); 56 | throw new RuntimeException("Error loading address via service " + service); 57 | } 58 | 59 | log("Finished loading address via service " + service); 60 | return new AddressVerificationResponse("Verification response from service " + service); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/InvoiceGenerator3_ThreadPool.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Customer; 6 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Invoice; 7 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.InvoiceTemplate; 8 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Order; 9 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.CustomerService; 10 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.InvoiceTemplateService; 11 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.OrderService; 12 | import java.util.concurrent.ExecutionException; 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.Executors; 15 | import java.util.concurrent.Future; 16 | 17 | public class InvoiceGenerator3_ThreadPool { 18 | 19 | static void main() throws InterruptedException, ExecutionException { 20 | InvoiceGenerator3_ThreadPool invoiceGenerator = 21 | new InvoiceGenerator3_ThreadPool( 22 | new OrderService(), new CustomerService(), new InvoiceTemplateService()); 23 | invoiceGenerator.createInvoice(10012, 61157, "en"); 24 | } 25 | 26 | private final OrderService orderService; 27 | private final CustomerService customerService; 28 | private final InvoiceTemplateService invoiceTemplateService; 29 | 30 | private final ExecutorService executor = Executors.newCachedThreadPool(); 31 | 32 | public InvoiceGenerator3_ThreadPool( 33 | OrderService orderService, 34 | CustomerService customerService, 35 | InvoiceTemplateService invoiceTemplateService) { 36 | this.orderService = orderService; 37 | this.customerService = customerService; 38 | this.invoiceTemplateService = invoiceTemplateService; 39 | } 40 | 41 | Invoice createInvoice(int orderId, int customerId, String language) 42 | throws InterruptedException, ExecutionException { 43 | log("Submitting tasks"); 44 | 45 | Future orderFuture = executor.submit(() -> orderService.getOrder(orderId)); 46 | 47 | Future customerFuture = 48 | executor.submit(() -> customerService.getCustomer(customerId)); 49 | 50 | Future invoiceTemplateFuture = 51 | executor.submit(() -> invoiceTemplateService.getTemplate(language)); 52 | 53 | log("Waiting for order"); 54 | Order order = orderFuture.get(); 55 | 56 | log("Waiting for customer"); 57 | Customer customer = customerFuture.get(); 58 | 59 | log("Waiting for template"); 60 | InvoiceTemplate invoiceTemplate = invoiceTemplateFuture.get(); 61 | 62 | log("Generating and returning invoice"); 63 | return Invoice.generate(order, customer, invoiceTemplate); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/InvoiceGenerator4_NewVirtualThreadPerTask.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Customer; 6 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Invoice; 7 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.InvoiceTemplate; 8 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Order; 9 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.CustomerService; 10 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.InvoiceTemplateService; 11 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.OrderService; 12 | import java.util.concurrent.ExecutionException; 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.Executors; 15 | import java.util.concurrent.Future; 16 | 17 | public class InvoiceGenerator4_NewVirtualThreadPerTask { 18 | 19 | static void main() throws InterruptedException, ExecutionException { 20 | InvoiceGenerator4_NewVirtualThreadPerTask invoiceGenerator = 21 | new InvoiceGenerator4_NewVirtualThreadPerTask( 22 | new OrderService(), new CustomerService(), new InvoiceTemplateService()); 23 | invoiceGenerator.createInvoice(10012, 61157, "en"); 24 | } 25 | 26 | private final OrderService orderService; 27 | private final CustomerService customerService; 28 | private final InvoiceTemplateService invoiceTemplateService; 29 | 30 | public InvoiceGenerator4_NewVirtualThreadPerTask( 31 | OrderService orderService, 32 | CustomerService customerService, 33 | InvoiceTemplateService invoiceTemplateService) { 34 | this.orderService = orderService; 35 | this.customerService = customerService; 36 | this.invoiceTemplateService = invoiceTemplateService; 37 | } 38 | 39 | Invoice createInvoice(int orderId, int customerId, String language) 40 | throws InterruptedException, ExecutionException { 41 | try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { 42 | log("Submitting tasks"); 43 | 44 | Future orderFuture = executor.submit(() -> orderService.getOrder(orderId)); 45 | Future customerFuture = 46 | executor.submit(() -> customerService.getCustomer(customerId)); 47 | Future invoiceTemplateFuture = 48 | executor.submit(() -> invoiceTemplateService.getTemplate(language)); 49 | 50 | log("Waiting for order"); 51 | Order order = orderFuture.get(); 52 | 53 | log("Waiting for customer"); 54 | Customer customer = customerFuture.get(); 55 | 56 | log("Waiting for template"); 57 | InvoiceTemplate invoiceTemplate = invoiceTemplateFuture.get(); 58 | 59 | log("Generating and returning invoice"); 60 | return Invoice.generate(order, customer, invoiceTemplate); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo3_suppliers/SupplierDeliveryTimeCheck4_NestedStructuredTaskScopeUsingScopedValue.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo3_suppliers; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo3_suppliers.model.SupplierDeliveryTime; 6 | import eu.happycoders.structuredconcurrency.demo3_suppliers.service.SupplierDeliveryTimeServiceUsingScopedValue; 7 | import java.util.Comparator; 8 | import java.util.List; 9 | import java.util.concurrent.StructuredTaskScope; 10 | import java.util.concurrent.StructuredTaskScope.Joiner; 11 | import java.util.concurrent.StructuredTaskScope.Subtask; 12 | 13 | public class SupplierDeliveryTimeCheck4_NestedStructuredTaskScopeUsingScopedValue { 14 | 15 | private static final boolean FAIL_ALL = false; 16 | 17 | static void main() throws Exception { 18 | SupplierDeliveryTimeCheck4_NestedStructuredTaskScopeUsingScopedValue supplierDeliveryTimeCheck = 19 | new SupplierDeliveryTimeCheck4_NestedStructuredTaskScopeUsingScopedValue( 20 | new SupplierDeliveryTimeServiceUsingScopedValue(FAIL_ALL)); 21 | List responses = 22 | supplierDeliveryTimeCheck.getSupplierDeliveryTimes( 23 | List.of("B004V9OA84", "0201310090", "0134685997"), 24 | List.of("A", "B", "C", "D", "E"), 25 | "t0p-s3cr3t"); 26 | log("Responses: " + responses); 27 | } 28 | 29 | private final SupplierDeliveryTimeServiceUsingScopedValue service; 30 | 31 | public SupplierDeliveryTimeCheck4_NestedStructuredTaskScopeUsingScopedValue( 32 | SupplierDeliveryTimeServiceUsingScopedValue service) { 33 | this.service = service; 34 | } 35 | 36 | public static final ScopedValue API_KEY = ScopedValue.newInstance(); 37 | 38 | List getSupplierDeliveryTimes( 39 | List productIds, List supplierIds, String apiKey) throws Exception { 40 | return ScopedValue.where(API_KEY, apiKey) 41 | .call(() -> getSupplierDeliveryTimes(productIds, supplierIds)); 42 | } 43 | 44 | List getSupplierDeliveryTimes( 45 | List productIds, List supplierIds) throws InterruptedException { 46 | try (var scope = 47 | StructuredTaskScope.open(Joiner.allSuccessfulOrThrow())) { 48 | productIds.forEach( 49 | productId -> scope.fork(() -> getSupplierDeliveryTime(productId, supplierIds))); 50 | 51 | return scope.join().map(Subtask::get).toList(); 52 | } 53 | } 54 | 55 | SupplierDeliveryTime getSupplierDeliveryTime(String productId, List supplierIds) 56 | throws InterruptedException { 57 | try (var scope = 58 | StructuredTaskScope.open( 59 | new BestResultJoiner<>( 60 | Comparator.comparing(SupplierDeliveryTime::deliveryTimeHours).reversed()))) { 61 | for (String supplierId : supplierIds) { 62 | scope.fork(() -> service.getDeliveryTime(productId, supplierId)); 63 | } 64 | 65 | return scope.join(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/InvoiceGenerator2b_CompletableFutureCancelling.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice; 2 | 3 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Customer; 4 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Invoice; 5 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.InvoiceTemplate; 6 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Order; 7 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.CustomerService; 8 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.InvoiceTemplateService; 9 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.OrderService; 10 | import java.util.concurrent.CancellationException; 11 | import java.util.concurrent.CompletableFuture; 12 | import java.util.concurrent.CompletionException; 13 | 14 | public class InvoiceGenerator2b_CompletableFutureCancelling { 15 | 16 | static void main() { 17 | InvoiceGenerator2b_CompletableFutureCancelling invoiceGenerator = 18 | new InvoiceGenerator2b_CompletableFutureCancelling( 19 | new OrderService(), new CustomerService(), new InvoiceTemplateService()); 20 | invoiceGenerator.createInvoiceAsync(10012, 61157, "en").join(); 21 | } 22 | 23 | private final OrderService orderService; 24 | private final CustomerService customerService; 25 | private final InvoiceTemplateService invoiceTemplateService; 26 | 27 | public InvoiceGenerator2b_CompletableFutureCancelling( 28 | OrderService orderService, 29 | CustomerService customerService, 30 | InvoiceTemplateService invoiceTemplateService) { 31 | this.orderService = orderService; 32 | this.customerService = customerService; 33 | this.invoiceTemplateService = invoiceTemplateService; 34 | } 35 | 36 | CompletableFuture createInvoiceAsync(int orderId, int customerId, String language) { 37 | CompletableFuture orderFuture = orderService.getOrderAsync(orderId); 38 | CompletableFuture customerFuture = customerService.getCustomerAsync(customerId); 39 | CompletableFuture templateFuture = 40 | invoiceTemplateService.getTemplateAsync(language); 41 | 42 | return CompletableFuture.allOf( 43 | orderFuture.exceptionally( 44 | e -> { 45 | // Attention: 46 | // cancel(true) will only cancel the CompletableFuture, not the underlying task! 47 | customerFuture.cancel(true); 48 | templateFuture.cancel(true); 49 | if (e instanceof CancellationException) { 50 | return null; 51 | } else { 52 | throw new CompletionException(e); 53 | } 54 | }), 55 | customerFuture.exceptionally( 56 | e -> { 57 | // Attention: 58 | // cancel(true) will only cancel the CompletableFuture, not the underlying task! 59 | orderFuture.cancel(true); 60 | templateFuture.cancel(true); 61 | if (e instanceof CancellationException) { 62 | return null; 63 | } else { 64 | throw new CompletionException(e); 65 | } 66 | }), 67 | templateFuture.exceptionally( 68 | e -> { 69 | // Attention: 70 | // cancel(true) will only cancel the CompletableFuture, not the underlying task! 71 | customerFuture.cancel(true); 72 | orderFuture.cancel(true); 73 | if (e instanceof CancellationException) { 74 | return null; 75 | } else { 76 | throw new CompletionException(e); 77 | } 78 | })) 79 | .thenCompose( 80 | _ -> { 81 | Order order = orderFuture.resultNow(); 82 | Customer customer = customerFuture.resultNow(); 83 | InvoiceTemplate template = templateFuture.resultNow(); 84 | Invoice invoice = Invoice.generate(order, customer, template); 85 | 86 | return CompletableFuture.completedFuture(invoice); 87 | }); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/InvoiceGenerator4b_NewVirtualThreadPerTaskCancelling_NoHints.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Customer; 6 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Invoice; 7 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.InvoiceTemplate; 8 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Order; 9 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.CustomerService; 10 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.InvoiceTemplateService; 11 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.OrderService; 12 | import java.util.concurrent.ExecutorService; 13 | import java.util.concurrent.Executors; 14 | import java.util.concurrent.Future; 15 | import java.util.concurrent.Future.State; 16 | 17 | public class InvoiceGenerator4b_NewVirtualThreadPerTaskCancelling_NoHints { 18 | 19 | static void main() throws Throwable { 20 | InvoiceGenerator4b_NewVirtualThreadPerTaskCancelling_NoHints invoiceGenerator = 21 | new InvoiceGenerator4b_NewVirtualThreadPerTaskCancelling_NoHints( 22 | new OrderService(), new CustomerService(), new InvoiceTemplateService()); 23 | invoiceGenerator.createInvoice(10012, 61157, "en"); 24 | } 25 | 26 | private final OrderService orderService; 27 | private final CustomerService customerService; 28 | private final InvoiceTemplateService invoiceTemplateService; 29 | 30 | public InvoiceGenerator4b_NewVirtualThreadPerTaskCancelling_NoHints( 31 | OrderService orderService, 32 | CustomerService customerService, 33 | InvoiceTemplateService invoiceTemplateService) { 34 | this.orderService = orderService; 35 | this.customerService = customerService; 36 | this.invoiceTemplateService = invoiceTemplateService; 37 | } 38 | 39 | Invoice createInvoice(int orderId, int customerId, String language) throws Throwable { 40 | Future orderFuture; 41 | Future customerFuture; 42 | Future invoiceTemplateFuture; 43 | 44 | try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { 45 | log("Submitting tasks"); 46 | 47 | orderFuture = 48 | executor.submit( 49 | () -> { 50 | try { 51 | return orderService.getOrder(orderId); 52 | } catch (Exception e) { 53 | executor.shutdownNow(); 54 | throw e; 55 | } 56 | }); 57 | 58 | customerFuture = 59 | executor.submit( 60 | () -> { 61 | try { 62 | return customerService.getCustomer(customerId); 63 | } catch (Exception e) { 64 | executor.shutdownNow(); 65 | throw e; 66 | } 67 | }); 68 | 69 | invoiceTemplateFuture = 70 | executor.submit( 71 | () -> { 72 | try { 73 | return invoiceTemplateService.getTemplate(language); 74 | } catch (Exception e) { 75 | executor.shutdownNow(); 76 | throw e; 77 | } 78 | }); 79 | 80 | log("Waiting for executor to shut down..."); 81 | } 82 | 83 | if (orderFuture.state() == State.SUCCESS 84 | && customerFuture.state() == State.SUCCESS 85 | && invoiceTemplateFuture.state() == State.SUCCESS) { 86 | Order order = orderFuture.get(); 87 | Customer customer = customerFuture.get(); 88 | InvoiceTemplate invoiceTemplate = invoiceTemplateFuture.get(); 89 | 90 | log("Generating and returning invoice"); 91 | return Invoice.generate(order, customer, invoiceTemplate); 92 | } else if (orderFuture.state() == State.FAILED 93 | && !(orderFuture.exceptionNow() instanceof InterruptedException)) { 94 | throw orderFuture.exceptionNow(); 95 | } else if (customerFuture.state() == State.FAILED 96 | && !(customerFuture.exceptionNow() instanceof InterruptedException)) { 97 | throw customerFuture.exceptionNow(); 98 | } else if (invoiceTemplateFuture.state() == State.FAILED) { 99 | throw invoiceTemplateFuture.exceptionNow(); 100 | } else if (orderFuture.state() == State.FAILED) { 101 | throw orderFuture.exceptionNow(); 102 | } else if (customerFuture.state() == State.FAILED) { 103 | throw customerFuture.exceptionNow(); 104 | } else { 105 | throw new IllegalStateException(); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/InvoiceGenerator4b_NewVirtualThreadPerTaskCancelling.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.structuredconcurrency.demo1_invoice; 2 | 3 | import static eu.happycoders.structuredconcurrency.util.SimpleLogger.log; 4 | 5 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Customer; 6 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Invoice; 7 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.InvoiceTemplate; 8 | import eu.happycoders.structuredconcurrency.demo1_invoice.model.Order; 9 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.CustomerService; 10 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.InvoiceTemplateService; 11 | import eu.happycoders.structuredconcurrency.demo1_invoice.service.OrderService; 12 | import java.util.concurrent.ExecutorService; 13 | import java.util.concurrent.Executors; 14 | import java.util.concurrent.Future; 15 | import java.util.concurrent.Future.State; 16 | 17 | public class InvoiceGenerator4b_NewVirtualThreadPerTaskCancelling { 18 | 19 | static void main() throws Throwable { 20 | InvoiceGenerator4b_NewVirtualThreadPerTaskCancelling invoiceGenerator = 21 | new InvoiceGenerator4b_NewVirtualThreadPerTaskCancelling( 22 | new OrderService(), new CustomerService(), new InvoiceTemplateService()); 23 | invoiceGenerator.createInvoice(10012, 61157, "en"); 24 | } 25 | 26 | private final OrderService orderService; 27 | private final CustomerService customerService; 28 | private final InvoiceTemplateService invoiceTemplateService; 29 | 30 | public InvoiceGenerator4b_NewVirtualThreadPerTaskCancelling( 31 | OrderService orderService, 32 | CustomerService customerService, 33 | InvoiceTemplateService invoiceTemplateService) { 34 | this.orderService = orderService; 35 | this.customerService = customerService; 36 | this.invoiceTemplateService = invoiceTemplateService; 37 | } 38 | 39 | Invoice createInvoice(int orderId, int customerId, String language) throws Throwable { 40 | Future orderFuture; 41 | Future customerFuture; 42 | Future invoiceTemplateFuture; 43 | 44 | try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { 45 | log("Submitting tasks"); 46 | 47 | orderFuture = 48 | executor.submit( 49 | () -> { 50 | try { 51 | return orderService.getOrder(orderId); 52 | } catch (Exception e) { 53 | executor.shutdownNow(); 54 | throw e; 55 | } 56 | }); 57 | 58 | // Attention, possible race conditions here: 59 | // If the thread above starts and fails and calls executor.shutdownNow() before the following 60 | // submit() is called, then submit() will throw a RejectedExecutionException. 61 | 62 | customerFuture = 63 | executor.submit( 64 | () -> { 65 | try { 66 | return customerService.getCustomer(customerId); 67 | } catch (Exception e) { 68 | executor.shutdownNow(); 69 | throw e; 70 | } 71 | }); 72 | 73 | // Attention, possible race conditions here: 74 | // If one of the threads above starts and fails and calls executor.shutdownNow() before the 75 | // following submit() is called, then submit() will throw a RejectedExecutionException. 76 | 77 | invoiceTemplateFuture = 78 | executor.submit( 79 | () -> { 80 | try { 81 | return invoiceTemplateService.getTemplate(language); 82 | } catch (Exception e) { 83 | executor.shutdownNow(); 84 | throw e; 85 | } 86 | }); 87 | 88 | log("Waiting for executor to shut down..."); 89 | } 90 | 91 | if (orderFuture.state() == State.SUCCESS 92 | && customerFuture.state() == State.SUCCESS 93 | && invoiceTemplateFuture.state() == State.SUCCESS) { 94 | Order order = orderFuture.get(); 95 | Customer customer = customerFuture.get(); 96 | InvoiceTemplate invoiceTemplate = invoiceTemplateFuture.get(); 97 | 98 | log("Generating and returning invoice"); 99 | return Invoice.generate(order, customer, invoiceTemplate); 100 | } else if (orderFuture.state() == State.FAILED 101 | && !(orderFuture.exceptionNow() instanceof InterruptedException)) { 102 | throw orderFuture.exceptionNow(); 103 | } else if (customerFuture.state() == State.FAILED 104 | && !(customerFuture.exceptionNow() instanceof InterruptedException)) { 105 | throw customerFuture.exceptionNow(); 106 | } else if (invoiceTemplateFuture.state() == State.FAILED) { 107 | throw invoiceTemplateFuture.exceptionNow(); 108 | } else if (orderFuture.state() == State.FAILED) { 109 | throw orderFuture.exceptionNow(); 110 | } else if (customerFuture.state() == State.FAILED) { 111 | throw customerFuture.exceptionNow(); 112 | } else { 113 | throw new IllegalStateException(); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Structure Concurrency Examples 2 | 3 | This repository contains a collection of examples demonstrating _Structured Concurrency_, a feature introduced as a preview in [Java 21](https://www.happycoders.eu/java/java-21-features/), with major enhancements in [Java 25](https://www.happycoders.eu/java/java-25-features/). 4 | 5 | A comprehensive explanation of Structured Concurrency is available in this article:\ 6 | 👉 [Structured Concurrency in Java](https://www.happycoders.eu/java/structured-concurrency-structuredtaskscope/) 7 | 8 | These examples are also used in my presentations, which is why not all of the code is directly referenced in the article. 9 | 10 | 11 | ## Java 21 vs. Java 25 12 | 13 | As mentioned above, *Structured Concurrency* underwent significant changes in Java 25. 14 | 15 | - The `main` branch contains the updated examples for Java 25. 16 | - The last commit compatible with Java 21–24 is available under the tag [`java-21`](https://github.com/SvenWoltmann/structured-concurrency/tree/java-21). 17 | 18 | To check out the Java 21 version after cloning the repository, run: 19 | 20 | ```bash 21 | git checkout java-21 22 | ``` 23 | 24 | ## Compile and Run from the Command Line 25 | 26 | To compile the examples (replace `25` with your Java version): 27 | 28 | Linux/macOS (using '/' to break up lines): 29 | 30 | ```bash 31 | javac --enable-preview --source 25 -d target/classes \ 32 | src/main/java/eu/happycoders/structuredconcurrency/util/*.java \ 33 | src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/model/*.java \ 34 | src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/service/*.java \ 35 | src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/*.java \ 36 | src/main/java/eu/happycoders/structuredconcurrency/demo2_address/model/*.java \ 37 | src/main/java/eu/happycoders/structuredconcurrency/demo2_address/service/*.java \ 38 | src/main/java/eu/happycoders/structuredconcurrency/demo2_address/*.java \ 39 | src/main/java/eu/happycoders/structuredconcurrency/demo3_suppliers/model/*.java \ 40 | src/main/java/eu/happycoders/structuredconcurrency/demo3_suppliers/service/*.java \ 41 | src/main/java/eu/happycoders/structuredconcurrency/demo3_suppliers/*.java 42 | ``` 43 | 44 | Windows (using '^' to break up lines): 45 | 46 | ```bash 47 | javac --enable-preview --source 25 -d target/classes ^ 48 | src/main/java/eu/happycoders/structuredconcurrency/util/*.java ^ 49 | src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/model/*.java ^ 50 | src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/service/*.java ^ 51 | src/main/java/eu/happycoders/structuredconcurrency/demo1_invoice/*.java ^ 52 | src/main/java/eu/happycoders/structuredconcurrency/demo2_address/model/*.java ^ 53 | src/main/java/eu/happycoders/structuredconcurrency/demo2_address/service/*.java ^ 54 | src/main/java/eu/happycoders/structuredconcurrency/demo2_address/*.java ^ 55 | src/main/java/eu/happycoders/structuredconcurrency/demo3_suppliers/model/*.java ^ 56 | src/main/java/eu/happycoders/structuredconcurrency/demo3_suppliers/service/*.java ^ 57 | src/main/java/eu/happycoders/structuredconcurrency/demo3_suppliers/*.java 58 | ``` 59 | 60 | Run the demos as follows: 61 | 62 | ```bash 63 | java -cp target/classes eu.happycoders.structuredconcurrency/demo1_invoice/InvoiceGenerator3_ThreadPool 64 | java -cp target/classes --enable-preview eu.happycoders.structuredconcurrency/demo1_invoice/InvoiceGenerator5_StructuredTaskScope 65 | java -cp target/classes --enable-preview eu.happycoders.structuredconcurrency/demo2_address/AddressVerification2_AnySuccessfulResult 66 | java -cp target/classes --enable-preview eu.happycoders.structuredconcurrency/demo3_suppliers/SupplierDeliveryTimeCheck2_StructuredTaskScope 67 | java -cp target/classes --enable-preview eu.happycoders.structuredconcurrency/demo3_suppliers/SupplierDeliveryTimeCheck3_NestedStructuredTaskScope 68 | java -cp target/classes --enable-preview eu.happycoders.structuredconcurrency/demo3_suppliers/SupplierDeliveryTimeCheck4_NestedStructuredTaskScopeUsingScopedValue 69 | ``` 70 | 71 | ## Java Downloads 72 | 73 | Java 25 Early Access: https://jdk.java.net/25/ 74 | 75 | Java 24: https://jdk.java.net/24/ 76 | 77 | Archive of older versions: https://jdk.java.net/archive/ 78 | 79 | 80 | ### Java Version Management 81 | 82 | - **Linux/macOS:** Use [SDKMAN!](https://sdkman.io/) to manage multiple Java versions. 83 | 84 | - **Windows:** Follow [this guide](https://www.happycoders.eu/java/how-to-switch-multiple-java-versions-windows/) to switch between Java versions. 85 | 86 | 87 | ## Related Repositories (Project Loom) 88 | 89 | Explore more Loom-related code: 90 | 91 | **Virtual Threads:** 92 | 93 | * https://github.com/SvenWoltmann/virtual-threads 94 | * https://github.com/SvenWoltmann/virtual-threads-quarkus 95 | * https://github.com/SvenWoltmann/virtual-threads-spring 96 | 97 | **Scoped Values:** 98 | 99 | * https://github.com/SvenWoltmann/scoped-values 100 | 101 | 102 | ## Other Resources 103 | 104 | ### Java Versions PDF Cheat Sheet 105 | 106 | **Stay up-to-date** with the latest Java features with [this PDF Cheat Sheet](https://www.happycoders.eu/java-versions/)! 107 | 108 | [Java Versions PDF Cheat Sheet Mockup](https://www.happycoders.eu/java-versions/) 109 | 110 | * Avoid lengthy research with this **concise overview of all Java versions up to Java 23**. 111 | * **Discover the innovative features** of each new Java version, summarized on a single page. 112 | * **Impress your team** with your up-to-date knowledge of the latest Java version. 113 | 114 | 👉 [Download the Java Versions PDF](https://www.happycoders.eu/java-versions/)
115 | 116 | _(Hier geht's zur deutschen Version → [Java-Versionen PDF](https://www.happycoders.eu/de/java-versionen/))_ 117 | 118 | 119 | ###
The Big O Cheat Sheet 120 | 121 | With this [1-page PDF cheat sheet](https://www.happycoders.eu/big-o-cheat-sheet/), you'll always have the **7 most important complexity classes** at a glance. 122 | 123 | [Big O PDF Cheat Sheet Mockup](https://www.happycoders.eu/big-o-cheat-sheet/) 124 | 125 | * **Always choose the most efficient data structures** and thus increase the performance of your applications. 126 | * **Be prepared for technical interviews** and confidently present your algorithm knowledge. 127 | * **Become a sought-after problem solver** and be known for systematically tackling complex problems. 128 | 129 | 👉 [Download the Big O Cheat Sheet](https://www.happycoders.eu/big-o-cheat-sheet/)
130 | 131 | _(Hier geht's zur deutschen Version → [O-Notation Cheat Sheet](https://www.happycoders.eu/de/o-notation-cheat-sheet/))_ 132 | 133 | 134 | ###
HappyCoders Newsletter 135 | 👉 Want to level up your Java skills? 136 | Sign up for the [HappyCoders newsletter](http://www.happycoders.eu/newsletter/) and get regular tips on programming, algorithms, and data structures! 137 | 138 | _(Hier geht's zur deutschen Version → [HappyCoders-Newsletter deutsch](https://www.happycoders.eu/de/newsletter/))_ 139 | 140 | 141 | ###
🇩🇪 An alle Java-Programmierer, die durch fundierte Kenntnisse über Datenstrukturen besseren Code schreiben wollen 142 | 143 | Trage dich jetzt auf die [Warteliste](https://www.happycoders.eu/de/mastering-data-structures-warteliste/) von „Mastering Data Structures in Java“ ein, und erhalte das beste Angebot! 144 | 145 | [Mastering Data Structures Mockup](https://www.happycoders.eu/de/mastering-data-structures-warteliste/) 146 | 147 | 👉 [Zur Warteliste](https://www.happycoders.eu/de/mastering-data-structures-warteliste/) 148 | 149 | --------------------------------------------------------------------------------