diff --git a/src/main/java/com/kousenit/virtualthreads/CustomerService.java b/src/main/java/com/kousenit/virtualthreads/CustomerService.java new file mode 100644 index 0000000..5c9bbd8 --- /dev/null +++ b/src/main/java/com/kousenit/virtualthreads/CustomerService.java @@ -0,0 +1,46 @@ +package com.kousenit.virtualthreads; + +import java.util.List; +import java.util.concurrent.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class CustomerService { + + public List fetchCustomerData(List customerIds) { + try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { + List> futures = customerIds.stream() + .map(id -> CompletableFuture.supplyAsync(() -> fetchCustomer(id), executor)) + .toList(); + + return CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)) + .thenApply(v -> futures.stream() + .map(CompletableFuture::join) + .toList()) + .join(); + } + } + + private Customer fetchCustomer(int id) { + try { + // Simulate API call or database query + Thread.sleep(1000); + return new Customer(id, "Customer " + id, "customer" + id + "@example.com"); + } catch (InterruptedException e) { + throw new RuntimeException("Thread interrupted while fetching customer " + id, e); + } + } + + // Customer as a record + public record Customer(int id, String name, String email) {} + + // Main method for demonstration + public static void main(String[] args) { + CustomerService service = new CustomerService(); + List customerIds = IntStream.rangeClosed(1, 5) + .boxed() + .collect(Collectors.toList()); + List customers = service.fetchCustomerData(customerIds); + customers.forEach(System.out::println); + } +} diff --git a/src/main/java/com/kousenit/virtualthreads/Demo.java b/src/main/java/com/kousenit/virtualthreads/VirtualThreadsDemo.java similarity index 95% rename from src/main/java/com/kousenit/virtualthreads/Demo.java rename to src/main/java/com/kousenit/virtualthreads/VirtualThreadsDemo.java index 5bbdeef..ee178bb 100644 --- a/src/main/java/com/kousenit/virtualthreads/Demo.java +++ b/src/main/java/com/kousenit/virtualthreads/VirtualThreadsDemo.java @@ -5,7 +5,7 @@ import java.util.stream.IntStream; // Example from: https://openjdk.org/jeps/444 (Virtual Threads) -public class Demo { +public class VirtualThreadsDemo { public static void main(String[] args) { long startTime = System.nanoTime(); try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { diff --git a/src/test/java/com/kousenit/generics/SafeVaragsDemoTest.java b/src/test/java/com/kousenit/generics/SafeVaragsVirtualThreadsDemoTest.java similarity index 97% rename from src/test/java/com/kousenit/generics/SafeVaragsDemoTest.java rename to src/test/java/com/kousenit/generics/SafeVaragsVirtualThreadsDemoTest.java index 302bf61..7b1360b 100644 --- a/src/test/java/com/kousenit/generics/SafeVaragsDemoTest.java +++ b/src/test/java/com/kousenit/generics/SafeVaragsVirtualThreadsDemoTest.java @@ -9,7 +9,7 @@ import static org.hamcrest.Matchers.arrayContaining; import static org.junit.jupiter.api.Assertions.assertThrows; -class SafeVaragsDemoTest { +class SafeVaragsVirtualThreadsDemoTest { private final SafeVaragsDemo demo = new SafeVaragsDemo(); private final LocalDate now = LocalDate.now(); diff --git a/src/test/java/com/kousenit/interfaces/PrivateDemoTest.java b/src/test/java/com/kousenit/interfaces/PrivateVirtualThreadsDemoTest.java similarity index 91% rename from src/test/java/com/kousenit/interfaces/PrivateDemoTest.java rename to src/test/java/com/kousenit/interfaces/PrivateVirtualThreadsDemoTest.java index b77f1e4..a0153cb 100644 --- a/src/test/java/com/kousenit/interfaces/PrivateDemoTest.java +++ b/src/test/java/com/kousenit/interfaces/PrivateVirtualThreadsDemoTest.java @@ -4,7 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -public class PrivateDemoTest { +public class PrivateVirtualThreadsDemoTest { private final PrivateDemo demo = new PrivateDemo(); @Test diff --git a/src/test/java/com/kousenit/virtualthreads/CustomerServiceTest.java b/src/test/java/com/kousenit/virtualthreads/CustomerServiceTest.java new file mode 100644 index 0000000..ce32fd6 --- /dev/null +++ b/src/test/java/com/kousenit/virtualthreads/CustomerServiceTest.java @@ -0,0 +1,46 @@ +package com.kousenit.virtualthreads; + +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.stream.IntStream; + +import static com.kousenit.virtualthreads.CustomerService.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class CustomerServiceTest { + + @Test + void testFetchCustomerData() { + CustomerService service = new CustomerService(); + List customerIds = IntStream.rangeClosed(1, 500) + .boxed() + .toList(); + + long startTime = System.currentTimeMillis(); + var customers = service.fetchCustomerData(customerIds); + long endTime = System.currentTimeMillis(); + + // Check if we got the correct number of customers + assertEquals(500, customers.size(), "Should fetch 500 customers"); + + // Verify first and last customer data + Customer firstCustomer = customers.getFirst(); + Customer lastCustomer = customers.getLast(); + + assertEquals(1, firstCustomer.id(), "First customer ID should be 1"); + assertEquals("Customer 1", firstCustomer.name(), "First customer name should match"); + assertEquals("customer1@example.com", firstCustomer.email(), "First customer email should match"); + + assertEquals(500, lastCustomer.id(), "Last customer ID should be 500"); + assertEquals("Customer 500", lastCustomer.name(), "Last customer name should match"); + assertEquals("customer500@example.com", lastCustomer.email(), "Last customer email should match"); + + // Check if the execution time is close to 1 second (allowing some margin) + long executionTime = endTime - startTime; + assertTrue(executionTime >= 1000 && executionTime < 1500, + "Execution time should be close to 1 second, but was " + executionTime + " ms"); + System.out.println("Execution time: " + executionTime + " ms"); + } +}