Which Jsoup method chain is typically used to establish a connection to a URL and retrieve its HTML content as a `Document` object?
Anonymous Quiz
43%
Jsoup.parse(url)
14%
Jsoup.load(url)
43%
Jsoup.connect(url).get()
0%
Jsoup.fetch(url)
π Day 27 is here, and we're diving into a game-changer: Spring Boot! β¨
Tired of complex configurations? Spring Boot simplifies Java development, letting you build robust, production-ready applications with minimal fuss. It's built on top of the Spring Framework but takes away much of the boilerplate thanks to auto-configuration and embedded servers. Let's write our first 'Hello World' with Spring Boot and see its magic!
```java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
// The @SpringBootApplication annotation is a convenience annotation that combines:
// @Configuration, @EnableAutoConfiguration, and @ComponentScan.
@SpringBootApplication
@RestController // Makes this class capable of handling web requests
public class Day27Application {
public static void main(String[] args) {
SpringApplication.run(Day27Application.class, args);
}
@GetMapping("/hello") // Maps HTTP GET requests to the /hello path
public String hello() {
return "Hello from Spring Boot!";
}
}
```
Output example:
(After running the application and navigating to `http://localhost:8080/hello` in your browser or using `curl`):
`Hello from Spring Boot!`
π€ Small challenge:
1. Add another endpoint, e.g., `/greet` that returns "Greetings, Java Learner!" π‘
2. Bonus: Try changing the default port (8080) to something else (e.g., 8081) using an `application.properties` file!
---
Tired of complex configurations? Spring Boot simplifies Java development, letting you build robust, production-ready applications with minimal fuss. It's built on top of the Spring Framework but takes away much of the boilerplate thanks to auto-configuration and embedded servers. Let's write our first 'Hello World' with Spring Boot and see its magic!
```java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
// The @SpringBootApplication annotation is a convenience annotation that combines:
// @Configuration, @EnableAutoConfiguration, and @ComponentScan.
@SpringBootApplication
@RestController // Makes this class capable of handling web requests
public class Day27Application {
public static void main(String[] args) {
SpringApplication.run(Day27Application.class, args);
}
@GetMapping("/hello") // Maps HTTP GET requests to the /hello path
public String hello() {
return "Hello from Spring Boot!";
}
}
```
Output example:
(After running the application and navigating to `http://localhost:8080/hello` in your browser or using `curl`):
`Hello from Spring Boot!`
π€ Small challenge:
1. Add another endpoint, e.g., `/greet` that returns "Greetings, Java Learner!" π‘
2. Bonus: Try changing the default port (8080) to something else (e.g., 8081) using an `application.properties` file!
---
Here's your Telegram post for Day 30!
---
π **Congratulations on Reaching Day 30!** π
You've made incredible progress in just thirty days, diving into Java fundamentals! π Now it's time to consolidate all your learning with a final project. How about building a simple *Command-Line To-Do List Manager*? π It's a fantastic way to apply your understanding of classes, objects, methods, `ArrayList`s, and user input. Let's review some core concepts and get you thinking!
Hereβs a quick look at a basic `Task` class and how you might manage a list of them:
```java
import java.util.ArrayList;
import java.util.List;
// Represents a single To-Do item
class Task {
private String description;
private boolean isCompleted;
public Task(String description) {
this.description = description;
this.isCompleted = false; // By default, a new task is not completed
}
public String getDescription() {
return description;
}
public boolean isCompleted() {
return isCompleted;
}
public void markCompleted() {
this.isCompleted = true;
}
@Override
public String toString() {
return "[" + (isCompleted ? "β " : "β³") + "] " + description;
}
}
// Main class to demonstrate To-Do list management
public class Day30Review {
public static void main(String[] args) {
List<Task> myTasks = new ArrayList<>(); // Use ArrayList for dynamic size
System.out.println("--- Initial To-Do List ---");
myTasks.add(new Task("Learn Java basics"));
myTasks.add(new Task("Start final project idea"));
myTasks.add(new Task("Review OOP concepts"));
for (Task task : myTasks) {
System.out.println(task);
}
System.out.println("n--- Updating Task Status ---");
// Mark the first task as completed
if (!myTasks.isEmpty()) {
myTasks.get(0).markCompleted();
System.out.println("Marked '" + myTasks.get(0).getDescription() + "' as completed.");
}
System.out.println("n--- Updated To-Do List ---");
for (Task task : myTasks) {
System.out.println(task);
}
}
}
```
Output example:
```
--- Initial To-Do List ---
[β³] Learn Java basics
[β³] Start final project idea
[β³] Review OOP concepts
--- Updating Task Status ---
Marked 'Learn Java basics' as completed.
--- Updated To-Do List ---
[β ] Learn Java basics
[β³] Start final project idea
[β³] Review OOP concepts
```
### Small Challenge:
Your challenge for your To-Do List Manager:
1. **Add User Input:** Implement `Scanner` to allow users to add new tasks.
2. **Remove Tasks:** Create a method that allows users to remove tasks by their index in the list.
3. **List by Status:** Add functionality to display *only* completed tasks or *only* pending tasks.
Ready to build something awesome? You've got this! πͺ Don't forget to share your progress! #JavaLearning #Day30 #FinalProject #CodingChallenge
---
---
π **Congratulations on Reaching Day 30!** π
You've made incredible progress in just thirty days, diving into Java fundamentals! π Now it's time to consolidate all your learning with a final project. How about building a simple *Command-Line To-Do List Manager*? π It's a fantastic way to apply your understanding of classes, objects, methods, `ArrayList`s, and user input. Let's review some core concepts and get you thinking!
Hereβs a quick look at a basic `Task` class and how you might manage a list of them:
```java
import java.util.ArrayList;
import java.util.List;
// Represents a single To-Do item
class Task {
private String description;
private boolean isCompleted;
public Task(String description) {
this.description = description;
this.isCompleted = false; // By default, a new task is not completed
}
public String getDescription() {
return description;
}
public boolean isCompleted() {
return isCompleted;
}
public void markCompleted() {
this.isCompleted = true;
}
@Override
public String toString() {
return "[" + (isCompleted ? "β " : "β³") + "] " + description;
}
}
// Main class to demonstrate To-Do list management
public class Day30Review {
public static void main(String[] args) {
List<Task> myTasks = new ArrayList<>(); // Use ArrayList for dynamic size
System.out.println("--- Initial To-Do List ---");
myTasks.add(new Task("Learn Java basics"));
myTasks.add(new Task("Start final project idea"));
myTasks.add(new Task("Review OOP concepts"));
for (Task task : myTasks) {
System.out.println(task);
}
System.out.println("n--- Updating Task Status ---");
// Mark the first task as completed
if (!myTasks.isEmpty()) {
myTasks.get(0).markCompleted();
System.out.println("Marked '" + myTasks.get(0).getDescription() + "' as completed.");
}
System.out.println("n--- Updated To-Do List ---");
for (Task task : myTasks) {
System.out.println(task);
}
}
}
```
Output example:
```
--- Initial To-Do List ---
[β³] Learn Java basics
[β³] Start final project idea
[β³] Review OOP concepts
--- Updating Task Status ---
Marked 'Learn Java basics' as completed.
--- Updated To-Do List ---
[β ] Learn Java basics
[β³] Start final project idea
[β³] Review OOP concepts
```
### Small Challenge:
Your challenge for your To-Do List Manager:
1. **Add User Input:** Implement `Scanner` to allow users to add new tasks.
2. **Remove Tasks:** Create a method that allows users to remove tasks by their index in the list.
3. **List by Status:** Add functionality to display *only* completed tasks or *only* pending tasks.
Ready to build something awesome? You've got this! πͺ Don't forget to share your progress! #JavaLearning #Day30 #FinalProject #CodingChallenge
---
Which of the following concepts is NOT directly demonstrated or primarily used in the provided `Day30Review` Java code for the To-Do List idea?
Anonymous Quiz
33%
A. Object-Oriented Programming (OOP) principles like encapsulation.
0%
B. Dynamic data structures using `ArrayList`.
33%
C. Basic input/output operations for user interaction.
33%
D. Iterating over collections using a for-each loop.
Here's your Telegram post for Day 31 of your Java learning series!
---
π Day 31: Ready to unlock the next level of Java concurrency?
Traditional threads are heavy, but Project Loom introduces **Virtual Threads**, lightweight wonders that revolutionize how we build scalable applications! π€― Imagine handling millions of concurrent tasks with ease, dramatically simplifying your code. We'll explore how Loom is driving a **Reactive Renaissance**, making complex asynchronous systems more manageable and efficient. Get ready to architect scalable futures! β¨
```java
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
public class VirtualThreadsDemo {
public static void main(String[] args) throws InterruptedException {
int numberOfTasks = 1_000_000; // Let's create a million "lightweight" tasks!
System.out.println("Starting " + numberOfTasks + " virtual tasks...");
Instant start = Instant.now();
// Using Executors.newVirtualThreadPerTaskExecutor() creates a new virtual thread
// for each submitted task, showcasing Loom's efficiency.
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, numberOfTasks).forEach(i ->
executor.submit(() -> {
// Simulate some work, perhaps a tiny delay or computation
try {
// In a real scenario, this might involve I/O operations
// which Virtual Threads are designed to efficiently handle.
// Thread.sleep(1); // Uncomment to simulate a tiny blocking operation per task
} catch (Exception e) {
Thread.currentThread().interrupt();
}
// System.out.println("Task " + i + " on " + Thread.currentThread().getName());
// (Commented out to avoid overwhelming console output)
})
);
} // The ExecutorService is automatically shut down here, waiting for all tasks.
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
System.out.println("All " + numberOfTasks + " tasks completed in " + duration.toMillis() + " ms.");
}
}
```
Output example:
```
Starting 1000000 virtual tasks...
All 1000000 tasks completed in XXX ms.
```
*(The `XXX` will be a relatively small number, demonstrating the speed of creating and managing a million virtual threads compared to traditional threads.)*
π‘ **Challenge Time!**
Modify the code to:
1. Run the same `numberOfTasks` using a traditional thread pool, e.g., `Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())` or `Executors.newCachedThreadPool()`.
2. Compare the execution time and observe any differences. What limitations or overheads do you encounter with traditional threads that Virtual Threads overcome? (Hint: Think about thread creation cost and memory footprint!).
---
---
π Day 31: Ready to unlock the next level of Java concurrency?
Traditional threads are heavy, but Project Loom introduces **Virtual Threads**, lightweight wonders that revolutionize how we build scalable applications! π€― Imagine handling millions of concurrent tasks with ease, dramatically simplifying your code. We'll explore how Loom is driving a **Reactive Renaissance**, making complex asynchronous systems more manageable and efficient. Get ready to architect scalable futures! β¨
```java
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
public class VirtualThreadsDemo {
public static void main(String[] args) throws InterruptedException {
int numberOfTasks = 1_000_000; // Let's create a million "lightweight" tasks!
System.out.println("Starting " + numberOfTasks + " virtual tasks...");
Instant start = Instant.now();
// Using Executors.newVirtualThreadPerTaskExecutor() creates a new virtual thread
// for each submitted task, showcasing Loom's efficiency.
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, numberOfTasks).forEach(i ->
executor.submit(() -> {
// Simulate some work, perhaps a tiny delay or computation
try {
// In a real scenario, this might involve I/O operations
// which Virtual Threads are designed to efficiently handle.
// Thread.sleep(1); // Uncomment to simulate a tiny blocking operation per task
} catch (Exception e) {
Thread.currentThread().interrupt();
}
// System.out.println("Task " + i + " on " + Thread.currentThread().getName());
// (Commented out to avoid overwhelming console output)
})
);
} // The ExecutorService is automatically shut down here, waiting for all tasks.
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
System.out.println("All " + numberOfTasks + " tasks completed in " + duration.toMillis() + " ms.");
}
}
```
Output example:
```
Starting 1000000 virtual tasks...
All 1000000 tasks completed in XXX ms.
```
*(The `XXX` will be a relatively small number, demonstrating the speed of creating and managing a million virtual threads compared to traditional threads.)*
π‘ **Challenge Time!**
Modify the code to:
1. Run the same `numberOfTasks` using a traditional thread pool, e.g., `Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())` or `Executors.newCachedThreadPool()`.
2. Compare the execution time and observe any differences. What limitations or overheads do you encounter with traditional threads that Virtual Threads overcome? (Hint: Think about thread creation cost and memory footprint!).
---
Here's your Telegram post for Day 36 of your advanced Java learning series! π
---
π Day 36: Time to level up your concurrency game! Traditional thread pools hit limits, but what if you could have *millions* of lightweight threads? π§΅ Today, we're diving deep into **Project Loom's Virtual Threads**! β¨ Forget blocking I/O woes and embrace hyperscale concurrency with minimal resource overhead. Virtual Threads redefine how we write scalable Java applications, making blocking code efficient and easy to reason about. Get ready to unlock new levels of performance!
```java
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
public class VirtualThreadsExample {
private static void performTask(int id) {
try {
// Simulate a blocking I/O operation (e.g., database call, network request)
// This blocking call will "park" the virtual thread, freeing up its carrier thread.
Thread.sleep(Duration.ofMillis(10));
// System.out.println("Task " + id + " completed by " + Thread.currentThread()); // Uncomment to see thread names
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) throws InterruptedException {
int numTasks = 10_000; // Let's launch 10,000 concurrent "blocking" tasks
System.out.println("--- Platform Threads Example (Limited Pool) ---");
Instant startPlatform = Instant.now();
// A small fixed pool to simulate resource contention with traditional threads
try (var executor = Executors.newFixedThreadPool(10)) {
IntStream.range(0, numTasks).forEach(i ->
executor.submit(() -> performTask(i))
);
} // Executor shuts down automatically
Instant endPlatform = Instant.now();
System.out.println("Platform Threads finished " + numTasks + " tasks in: " + Duration.between(startPlatform, endPlatform).toMillis() + " msn");
System.out.println("--- Virtual Threads Example ---");
Instant startVirtual = Instant.now();
// Executors.newVirtualThreadPerTaskExecutor() creates a new virtual thread for each task
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, numTasks).forEach(i ->
executor.submit(() -> performTask(i))
);
}
Instant endVirtual = Instant.now();
System.out.println("Virtual Threads finished " + numTasks + " tasks in: " + Duration.between(startVirtual, endVirtual).toMillis() + " ms");
}
}
```
Output example:
```
--- Platform Threads Example (Limited Pool) ---
Platform Threads finished 10000 tasks in: 10045 ms
--- Virtual Threads Example ---
Virtual Threads finished 10000 tasks in: 112 ms
```
*(Note: Actual times may vary based on your system and Java version. Java 21+ recommended for Project Loom features.)*
π‘ **Challenge:** Modify the `performTask` method to simulate a non-blocking operation (e.g., a simple CPU-bound calculation) instead of `Thread.sleep()`. How does the performance difference between platform and virtual threads change then? What does this tell you about the ideal use cases for virtual threads?
---
---
π Day 36: Time to level up your concurrency game! Traditional thread pools hit limits, but what if you could have *millions* of lightweight threads? π§΅ Today, we're diving deep into **Project Loom's Virtual Threads**! β¨ Forget blocking I/O woes and embrace hyperscale concurrency with minimal resource overhead. Virtual Threads redefine how we write scalable Java applications, making blocking code efficient and easy to reason about. Get ready to unlock new levels of performance!
```java
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
public class VirtualThreadsExample {
private static void performTask(int id) {
try {
// Simulate a blocking I/O operation (e.g., database call, network request)
// This blocking call will "park" the virtual thread, freeing up its carrier thread.
Thread.sleep(Duration.ofMillis(10));
// System.out.println("Task " + id + " completed by " + Thread.currentThread()); // Uncomment to see thread names
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) throws InterruptedException {
int numTasks = 10_000; // Let's launch 10,000 concurrent "blocking" tasks
System.out.println("--- Platform Threads Example (Limited Pool) ---");
Instant startPlatform = Instant.now();
// A small fixed pool to simulate resource contention with traditional threads
try (var executor = Executors.newFixedThreadPool(10)) {
IntStream.range(0, numTasks).forEach(i ->
executor.submit(() -> performTask(i))
);
} // Executor shuts down automatically
Instant endPlatform = Instant.now();
System.out.println("Platform Threads finished " + numTasks + " tasks in: " + Duration.between(startPlatform, endPlatform).toMillis() + " msn");
System.out.println("--- Virtual Threads Example ---");
Instant startVirtual = Instant.now();
// Executors.newVirtualThreadPerTaskExecutor() creates a new virtual thread for each task
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, numTasks).forEach(i ->
executor.submit(() -> performTask(i))
);
}
Instant endVirtual = Instant.now();
System.out.println("Virtual Threads finished " + numTasks + " tasks in: " + Duration.between(startVirtual, endVirtual).toMillis() + " ms");
}
}
```
Output example:
```
--- Platform Threads Example (Limited Pool) ---
Platform Threads finished 10000 tasks in: 10045 ms
--- Virtual Threads Example ---
Virtual Threads finished 10000 tasks in: 112 ms
```
*(Note: Actual times may vary based on your system and Java version. Java 21+ recommended for Project Loom features.)*
π‘ **Challenge:** Modify the `performTask` method to simulate a non-blocking operation (e.g., a simple CPU-bound calculation) instead of `Thread.sleep()`. How does the performance difference between platform and virtual threads change then? What does this tell you about the ideal use cases for virtual threads?
---
Day 37, advanced Java explorers! π Today, we're diving deep into a modern concurrency marvel: Project Loom's `ScopedValue`. Say goodbye to the complexities and potential leaks of `ThreadLocal` in concurrent scenarios. `ScopedValue` offers a cleaner, safer, and more performant way to share immutable data within a lexical scope, perfectly aligning with Java's new Structured Concurrency paradigm. Get ready to rethink how you pass context across your application! π‘
```java
import java.util.concurrent.Executors;
import java.util.concurrent.ScopedValue;
import java.util.concurrent.TimeUnit;
public class ScopedValueDemo {
// Declare a ScopedValue instance
private static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
public static void main(String[] args) throws InterruptedException {
System.out.println("Main thread - Before binding: " + USER_ID.orElse("N/A"));
// Bind a value to USER_ID within a specific scope
ScopedValue.where(USER_ID, "user-123")
.run(() -> {
System.out.println("Scoped block 1 - User ID: " + USER_ID.get());
// Virtual threads within this scope automatically "see" the bound value
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
System.out.println("Virtual Thread in Scoped block 1 - User ID: " + USER_ID.get());
processRequest(); // Method called within the scope
}).get(); // Wait for the virtual thread to complete
} catch (Exception e) {
e.printStackTrace();
}
// Nested scope demonstrates re-binding, which only affects the inner scope
ScopedValue.where(USER_ID, "user-456")
.run(() -> {
System.out.println("Nested Scoped block 2 - User ID: " + USER_ID.get());
});
System.out.println("Back in Scoped block 1 - User ID: " + USER_ID.get());
}); // The binding for "user-123" ends here
System.out.println("Main thread - After binding scope: " + USER_ID.orElse("N/A"));
// Demonstrate attempting to access from a new, unrelated virtual thread
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
System.out.println("Virtual Thread outside any explicit scope - User ID: " + USER_ID.orElse("N/A"));
}).get();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void processRequest() {
// This method will automatically see the ScopedValue if called within its scope
System.out.println("Processing request with User ID: " + USER_ID.orElse("N/A"));
}
}
```
Output example:
```
Main thread - Before binding: N/A
Scoped block 1 - User ID: user-123
Virtual Thread in Scoped block 1 - User ID: user-123
Processing request with User ID: user-123
Nested Scoped block 2 - User ID: user-456
Back in Scoped block 1 - User ID: user-123
Main thread - After binding scope: N/A
Virtual Thread outside any explicit scope - User ID: N/A
```
Small challenge:
Modify the `processRequest()` method to try binding `USER_ID` to a *new* value within itself using `ScopedValue.where(USER_ID, "temp-user").run(...)`. Observe the behavior when `processRequest()` finishes and control returns to its caller. What does this tell you about the immutable and lexically scoped nature of `ScopedValue` bindings, especially compared to `ThreadLocal`? π€
```java
import java.util.concurrent.Executors;
import java.util.concurrent.ScopedValue;
import java.util.concurrent.TimeUnit;
public class ScopedValueDemo {
// Declare a ScopedValue instance
private static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
public static void main(String[] args) throws InterruptedException {
System.out.println("Main thread - Before binding: " + USER_ID.orElse("N/A"));
// Bind a value to USER_ID within a specific scope
ScopedValue.where(USER_ID, "user-123")
.run(() -> {
System.out.println("Scoped block 1 - User ID: " + USER_ID.get());
// Virtual threads within this scope automatically "see" the bound value
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
System.out.println("Virtual Thread in Scoped block 1 - User ID: " + USER_ID.get());
processRequest(); // Method called within the scope
}).get(); // Wait for the virtual thread to complete
} catch (Exception e) {
e.printStackTrace();
}
// Nested scope demonstrates re-binding, which only affects the inner scope
ScopedValue.where(USER_ID, "user-456")
.run(() -> {
System.out.println("Nested Scoped block 2 - User ID: " + USER_ID.get());
});
System.out.println("Back in Scoped block 1 - User ID: " + USER_ID.get());
}); // The binding for "user-123" ends here
System.out.println("Main thread - After binding scope: " + USER_ID.orElse("N/A"));
// Demonstrate attempting to access from a new, unrelated virtual thread
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
System.out.println("Virtual Thread outside any explicit scope - User ID: " + USER_ID.orElse("N/A"));
}).get();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void processRequest() {
// This method will automatically see the ScopedValue if called within its scope
System.out.println("Processing request with User ID: " + USER_ID.orElse("N/A"));
}
}
```
Output example:
```
Main thread - Before binding: N/A
Scoped block 1 - User ID: user-123
Virtual Thread in Scoped block 1 - User ID: user-123
Processing request with User ID: user-123
Nested Scoped block 2 - User ID: user-456
Back in Scoped block 1 - User ID: user-123
Main thread - After binding scope: N/A
Virtual Thread outside any explicit scope - User ID: N/A
```
Small challenge:
Modify the `processRequest()` method to try binding `USER_ID` to a *new* value within itself using `ScopedValue.where(USER_ID, "temp-user").run(...)`. Observe the behavior when `processRequest()` finishes and control returns to its caller. What does this tell you about the immutable and lexically scoped nature of `ScopedValue` bindings, especially compared to `ThreadLocal`? π€
π **Day 38: Diving Deep into Java's Concurrency Universe!** π
Today, we're going beyond the basics of `volatile` and `synchronized` to explore the fascinating world of `VarHandle`. Introduced in Java 9, `VarHandle` provides a powerful, safe, and flexible way to perform atomic operations on fields and array elements, offering fine-grained control over memory ordering. It's the modern, safer alternative to `sun.misc.Unsafe` and empowers developers to write high-performance, low-latency concurrent code. Get ready to unlock new levels of control over your application's memory! βοΈ
Here's a quick peek at how `VarHandle` enables atomic updates:
```java
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
public class AtomicCounter {
private volatile int counter = 0; // VarHandle can access volatile fields
// VarHandle for the 'counter' field
private static final VarHandle COUNTER_HANDLE;
static {
try {
// Find the VarHandle for the 'counter' field in AtomicCounter class
COUNTER_HANDLE = MethodHandles.lookup().findVarHandle(
AtomicCounter.class, "counter", int.class);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}
public void increment() {
// Atomically adds 1 to the 'counter' field
COUNTER_HANDLE.getAndAdd(this, 1);
}
public int get() {
// Reads the current value of 'counter'
return (int) COUNTER_HANDLE.getVolatile(this);
}
public static void main(String[] args) throws InterruptedException {
AtomicCounter atomicCounter = new AtomicCounter();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
atomicCounter.increment();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final Counter Value: " + atomicCounter.get());
}
}
```
Output example:
```
Final Counter Value: 2000
```
**Small Challenge:** Modify the `increment()` method to use `compareAndSet` instead of `getAndAdd`. Hint: You'll need a loop to retry if another thread modifies the value before your update. How does the code change, and what are the implications for performance?
---
Today, we're going beyond the basics of `volatile` and `synchronized` to explore the fascinating world of `VarHandle`. Introduced in Java 9, `VarHandle` provides a powerful, safe, and flexible way to perform atomic operations on fields and array elements, offering fine-grained control over memory ordering. It's the modern, safer alternative to `sun.misc.Unsafe` and empowers developers to write high-performance, low-latency concurrent code. Get ready to unlock new levels of control over your application's memory! βοΈ
Here's a quick peek at how `VarHandle` enables atomic updates:
```java
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
public class AtomicCounter {
private volatile int counter = 0; // VarHandle can access volatile fields
// VarHandle for the 'counter' field
private static final VarHandle COUNTER_HANDLE;
static {
try {
// Find the VarHandle for the 'counter' field in AtomicCounter class
COUNTER_HANDLE = MethodHandles.lookup().findVarHandle(
AtomicCounter.class, "counter", int.class);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}
public void increment() {
// Atomically adds 1 to the 'counter' field
COUNTER_HANDLE.getAndAdd(this, 1);
}
public int get() {
// Reads the current value of 'counter'
return (int) COUNTER_HANDLE.getVolatile(this);
}
public static void main(String[] args) throws InterruptedException {
AtomicCounter atomicCounter = new AtomicCounter();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
atomicCounter.increment();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final Counter Value: " + atomicCounter.get());
}
}
```
Output example:
```
Final Counter Value: 2000
```
**Small Challenge:** Modify the `increment()` method to use `compareAndSet` instead of `getAndAdd`. Hint: You'll need a loop to retry if another thread modifies the value before your update. How does the code change, and what are the implications for performance?
---
Which of the following is a primary benefit of using Java Virtual Threads (Project Loom) over traditional platform threads for I/O-bound tasks?
Anonymous Quiz
33%
Significantly reduced resource consumption (memory for stack, OS threads) per concurrent task.
0%
Guaranteed real-time execution with lower latency.
67%
Automatic parallelization of CPU-bound computations across all available cores.
0%
Elimination of the need for garbage collection in concurrent applications.
Day 40: Level up your concurrency game! π Java's Virtual Threads, a cornerstone of Project Loom, are revolutionizing how we write scalable applications. Imagine handling millions of concurrent tasks without complex thread pools or resource exhaustion! They are lightweight, user-mode threads managed by the JVM, efficiently mapping to a small number of underlying platform threads. This architectural shift, combined with Structured Concurrency, is reimagining how Java thrives in the cloud-native era. Let's see them in action! β¨
```java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class VirtualThreadDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("Starting Virtual Thread Demo π");
// Use a new Virtual Thread per task executor for simplicity
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 5; i++) {
final int taskId = i;
executor.submit(() -> {
try {
System.out.println("Task " + taskId + " running on virtual thread: " + Thread.currentThread());
TimeUnit.MILLISECONDS.sleep(100 + (long)(Math.random() * 200)); // Simulate work
System.out.println("Task " + taskId + " finished.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Restore interrupt status
}
});
}
} // ExecutorService implicitly waits for all tasks to complete and then shuts down
System.out.println("All virtual tasks submitted. Main thread continuing...");
TimeUnit.SECONDS.sleep(1); // Give a moment for tasks to start showing output if they haven't already
System.out.println("Demo finished. Did you notice the simplicity and thread names? β¨");
}
}
```
Output example:
```
Starting Virtual Thread Demo π
Task 0 running on virtual thread: VirtualThread[<sequence_id>]
Task 1 running on virtual thread: VirtualThread[<sequence_id>]
Task 2 running on virtual thread: VirtualThread[<sequence_id>]
Task 3 running on virtual thread: VirtualThread[<sequence_id>]
Task 4 running on virtual thread: VirtualThread[<sequence_id>]
All virtual tasks submitted. Main thread continuing...
Task 0 finished.
Task 2 finished.
Task 1 finished.
Task 4 finished.
Task 3 finished.
Demo finished. Did you notice the simplicity and thread names? β¨
```
*(Note: The exact order of "Task X running" and "Task X finished" might vary due to concurrency.)*
**Small challenge:**
How would you create a virtual thread directly using `Thread.ofVirtual().start()` instead of `Executors.newVirtualThreadPerTaskExecutor()`? Can you modify the demo code to use this direct method for at least one of the tasks?
```java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class VirtualThreadDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("Starting Virtual Thread Demo π");
// Use a new Virtual Thread per task executor for simplicity
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 5; i++) {
final int taskId = i;
executor.submit(() -> {
try {
System.out.println("Task " + taskId + " running on virtual thread: " + Thread.currentThread());
TimeUnit.MILLISECONDS.sleep(100 + (long)(Math.random() * 200)); // Simulate work
System.out.println("Task " + taskId + " finished.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Restore interrupt status
}
});
}
} // ExecutorService implicitly waits for all tasks to complete and then shuts down
System.out.println("All virtual tasks submitted. Main thread continuing...");
TimeUnit.SECONDS.sleep(1); // Give a moment for tasks to start showing output if they haven't already
System.out.println("Demo finished. Did you notice the simplicity and thread names? β¨");
}
}
```
Output example:
```
Starting Virtual Thread Demo π
Task 0 running on virtual thread: VirtualThread[<sequence_id>]
Task 1 running on virtual thread: VirtualThread[<sequence_id>]
Task 2 running on virtual thread: VirtualThread[<sequence_id>]
Task 3 running on virtual thread: VirtualThread[<sequence_id>]
Task 4 running on virtual thread: VirtualThread[<sequence_id>]
All virtual tasks submitted. Main thread continuing...
Task 0 finished.
Task 2 finished.
Task 1 finished.
Task 4 finished.
Task 3 finished.
Demo finished. Did you notice the simplicity and thread names? β¨
```
*(Note: The exact order of "Task X running" and "Task X finished" might vary due to concurrency.)*
**Small challenge:**
How would you create a virtual thread directly using `Thread.ofVirtual().start()` instead of `Executors.newVirtualThreadPerTaskExecutor()`? Can you modify the demo code to use this direct method for at least one of the tasks?
Which of the following is a primary benefit of using `StructuredTaskScope` in Java for concurrent operations?
Anonymous Quiz
100%
It simplifies error handling and cancellation management across related child tasks.
0%
It automatically converts platform threads into virtual threads for better resource utilization.
0%
It provides a declarative way to define microservices architecture within a single JVM.
0%
It enables direct interaction with off-heap memory without Project Panama.
π **Day 42: Diving Deep into Java's Future!** π
Today, we're not just using Virtual Threads, we're *mastering* them! π§΅ Join us as we explore Project Loom's advanced scheduling nuances, critical contention scenarios, and cutting-edge monitoring strategies. Go beyond basic usage to truly understand how to optimize and observe your high-concurrency Java applications. Get ready to unlock unparalleled performance! β‘οΈ
Let's start with a simple demonstration setting the stage for understanding their operational aspects:
```java
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
public class VirtualThreadContentionDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("Starting Virtual Threads Simulation... π¦");
long startTime = System.nanoTime();
// Using a VirtualThreadPerTaskExecutor ensures each task gets a new virtual thread
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 5000)
.forEach(i -> executor.submit(() -> {
try {
// Simulate some work, potentially involving I/O or shared resource access
Thread.sleep(1);
System.out.println("Task " + i + " completed by Virtual Thread: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Task " + i + " interrupted.");
}
}));
} // The executor's close() method waits for all submitted tasks to complete
long endTime = System.nanoTime();
long durationMillis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
System.out.println("All 5000 virtual threads completed in " + durationMillis + " ms. π");
System.out.println("Simulation finished! β ");
}
}
```
Output example:
```
Starting Virtual Threads Simulation... π¦
Task 0 completed by Virtual Thread: VirtualThread[#21]/runnable@ForkJoinPool-1-worker-1
Task 1 completed by Virtual Thread: VirtualThread[#22]/runnable@ForkJoinPool-1-worker-2
Task 2 completed by Virtual Thread: VirtualThread[#23]/runnable@ForkJoinPool-1-worker-3
... (many more lines) ...
Task 4998 completed by Virtual Thread: VirtualThread[#5019]/runnable@ForkJoinPool-1-worker-3
Task 4999 completed by Virtual Thread: VirtualThread[#5020]/runnable@ForkJoinPool-1-worker-2
All 5000 virtual threads completed in 56 ms. π
Simulation finished! β
```
### Small Challenge:
The code above spins up many virtual threads performing simple tasks. If these tasks were to frequently contend for a shared, exclusive resource (e.g., a single database connection or an in-memory lock), how would you *proactively* identify and monitor these contention points and their impact on throughput using JVM diagnostic tools like JFR (Java Flight Recorder) or JConsole? What specific events or metrics would you look for to pinpoint bottlenecks? π€
---
Today, we're not just using Virtual Threads, we're *mastering* them! π§΅ Join us as we explore Project Loom's advanced scheduling nuances, critical contention scenarios, and cutting-edge monitoring strategies. Go beyond basic usage to truly understand how to optimize and observe your high-concurrency Java applications. Get ready to unlock unparalleled performance! β‘οΈ
Let's start with a simple demonstration setting the stage for understanding their operational aspects:
```java
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
public class VirtualThreadContentionDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("Starting Virtual Threads Simulation... π¦");
long startTime = System.nanoTime();
// Using a VirtualThreadPerTaskExecutor ensures each task gets a new virtual thread
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 5000)
.forEach(i -> executor.submit(() -> {
try {
// Simulate some work, potentially involving I/O or shared resource access
Thread.sleep(1);
System.out.println("Task " + i + " completed by Virtual Thread: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Task " + i + " interrupted.");
}
}));
} // The executor's close() method waits for all submitted tasks to complete
long endTime = System.nanoTime();
long durationMillis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
System.out.println("All 5000 virtual threads completed in " + durationMillis + " ms. π");
System.out.println("Simulation finished! β ");
}
}
```
Output example:
```
Starting Virtual Threads Simulation... π¦
Task 0 completed by Virtual Thread: VirtualThread[#21]/runnable@ForkJoinPool-1-worker-1
Task 1 completed by Virtual Thread: VirtualThread[#22]/runnable@ForkJoinPool-1-worker-2
Task 2 completed by Virtual Thread: VirtualThread[#23]/runnable@ForkJoinPool-1-worker-3
... (many more lines) ...
Task 4998 completed by Virtual Thread: VirtualThread[#5019]/runnable@ForkJoinPool-1-worker-3
Task 4999 completed by Virtual Thread: VirtualThread[#5020]/runnable@ForkJoinPool-1-worker-2
All 5000 virtual threads completed in 56 ms. π
Simulation finished! β
```
### Small Challenge:
The code above spins up many virtual threads performing simple tasks. If these tasks were to frequently contend for a shared, exclusive resource (e.g., a single database connection or an in-memory lock), how would you *proactively* identify and monitor these contention points and their impact on throughput using JVM diagnostic tools like JFR (Java Flight Recorder) or JConsole? What specific events or metrics would you look for to pinpoint bottlenecks? π€
---
Which of the following is NOT a primary benefit of Java's Project Loom Virtual Threads when dealing with I/O-bound applications?
Anonymous Quiz
33%
Reduced memory footprint compared to platform threads for high concurrency.
0%
Simplified sequential programming style for concurrent tasks.
67%
Significant increase in raw CPU processing power for compute-bound tasks.
0%
Improved server throughput by allowing more concurrent requests to be handled.
π Day 43: Dive Deep into Advanced Java! Today, we're exploring the future of concurrent programming with Project Loom! π§΅ Forget the complexities of traditional thread pools and embrace the power of Virtual Threads and `StructuredTaskScope`. Learn how to build highly scalable, robust, and readable concurrent applications with elegant error handling. Get ready to master the next generation of JVM concurrency! β¨
```java
import java.time.Duration;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException;
public class Day43Loom {
public static void main(String[] args) throws Exception {
System.out.println("Starting concurrent data fetch with StructuredTaskScope...");
// Use ShutdownOnFailure to ensure that if any subtask fails,
// all other running subtasks are cancelled.
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> userData = scope.fork(() -> {
System.out.println(" [Task 1] Fetching user data (on a Virtual Thread)...");
Thread.sleep(Duration.ofSeconds(2)); // Simulate network call
System.out.println(" [Task 1] User data fetched.");
return "User: Alice (ID: 101)";
});
Future<String> orderData = scope.fork(() -> {
System.out.println(" [Task 2] Fetching order data (on a Virtual Thread)...");
Thread.sleep(Duration.ofSeconds(3)); // Simulate another network call
System.out.println(" [Task 2] Order data fetched.");
return "Order: #XYZ789 (Items: 3)";
});
// Blocks until all forked tasks complete, or the scope is shut down due to a failure.
scope.join();
// Propagates any exception thrown by a subtask.
scope.throwIfFailed();
String user = userData.resultNow(); // Retrieves result without blocking further
String order = orderData.resultNow();
System.out.println("n--- Combined Results ---");
System.out.println(user);
System.out.println(order);
} catch (Exception e) {
System.err.println("nError during concurrent operation: " + e.getMessage());
// In a real application, you might log the error or return a default value.
}
System.out.println("nMain thread finished.");
}
}
```
Output example:
```
Starting concurrent data fetch with StructuredTaskScope...
[Task 1] Fetching user data (on a Virtual Thread)...
[Task 2] Fetching order data (on a Virtual Thread)...
[Task 1] User data fetched.
[Task 2] Order data fetched.
--- Combined Results ---
User: Alice (ID: 101)
Order: #XYZ789 (Items: 3)
Main thread finished.
```
π‘ **Challenge:** Modify the `orderData` task to throw a `RuntimeException` (e.g., `new RuntimeException("Database connection timed out!")`) after 1 second. Observe how `StructuredTaskScope.ShutdownOnFailure` handles the failure and what the main thread prints. How would you use `StructuredTaskScope.ShutdownOnSuccess` differently if you only cared about the first successful result?
---
```java
import java.time.Duration;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException;
public class Day43Loom {
public static void main(String[] args) throws Exception {
System.out.println("Starting concurrent data fetch with StructuredTaskScope...");
// Use ShutdownOnFailure to ensure that if any subtask fails,
// all other running subtasks are cancelled.
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> userData = scope.fork(() -> {
System.out.println(" [Task 1] Fetching user data (on a Virtual Thread)...");
Thread.sleep(Duration.ofSeconds(2)); // Simulate network call
System.out.println(" [Task 1] User data fetched.");
return "User: Alice (ID: 101)";
});
Future<String> orderData = scope.fork(() -> {
System.out.println(" [Task 2] Fetching order data (on a Virtual Thread)...");
Thread.sleep(Duration.ofSeconds(3)); // Simulate another network call
System.out.println(" [Task 2] Order data fetched.");
return "Order: #XYZ789 (Items: 3)";
});
// Blocks until all forked tasks complete, or the scope is shut down due to a failure.
scope.join();
// Propagates any exception thrown by a subtask.
scope.throwIfFailed();
String user = userData.resultNow(); // Retrieves result without blocking further
String order = orderData.resultNow();
System.out.println("n--- Combined Results ---");
System.out.println(user);
System.out.println(order);
} catch (Exception e) {
System.err.println("nError during concurrent operation: " + e.getMessage());
// In a real application, you might log the error or return a default value.
}
System.out.println("nMain thread finished.");
}
}
```
Output example:
```
Starting concurrent data fetch with StructuredTaskScope...
[Task 1] Fetching user data (on a Virtual Thread)...
[Task 2] Fetching order data (on a Virtual Thread)...
[Task 1] User data fetched.
[Task 2] Order data fetched.
--- Combined Results ---
User: Alice (ID: 101)
Order: #XYZ789 (Items: 3)
Main thread finished.
```
π‘ **Challenge:** Modify the `orderData` task to throw a `RuntimeException` (e.g., `new RuntimeException("Database connection timed out!")`) after 1 second. Observe how `StructuredTaskScope.ShutdownOnFailure` handles the failure and what the main thread prints. How would you use `StructuredTaskScope.ShutdownOnSuccess` differently if you only cared about the first successful result?
---
Welcome to Day 47 of our Java learning journey! π Today, we're diving into one of the most exciting advancements in modern Java: **Structured Concurrency and Virtual Threads (Project Loom)**. π‘ Tired of callback hell, complex error handling in async code, and debugging messy thread pools? Virtual Threads offer a lightweight, high-throughput solution, while Structured Concurrency brings clarity and robustness to concurrent task management. β¨ Let's reimagine how we write asynchronous Java!
Virtual Threads provide "threads for free," allowing you to write blocking code as if it were non-blocking. Structured Concurrency, especially with `StructuredTaskScope`, ensures that the lifecycle of child tasks is tied to their parent, drastically simplifying error propagation and cancellation.
Here's an example using `StructuredTaskScope.ShutdownOnFailure` to manage two concurrent virtual tasks:
```java
import java.time.Duration;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.TimeoutException;
// Requires --enable-preview --source 21 (or higher) to run
public class Day47StructuredConcurrency {
public static void main(String[] args) throws InterruptedException {
System.out.println("Starting main task at " + System.currentTimeMillis());
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
StructuredTaskScope.Subtask<String> task1 = scope.fork(() -> {
System.out.println("Task 1 (Virtual Thread) starting...");
Thread.sleep(Duration.ofSeconds(3)); // Simulate work
System.out.println("Task 1 completed.");
return "Data from Service A";
});
StructuredTaskScope.Subtask<String> task2 = scope.fork(() -> {
System.out.println("Task 2 (Virtual Thread) starting...");
Thread.sleep(Duration.ofSeconds(1)); // Simulate work
if (System.currentTimeMillis() % 2 != 0) { // Simulate occasional failure
throw new RuntimeException("Task 2 failed unexpectedly!");
}
System.out.println("Task 2 completed.");
return "Data from Service B";
});
scope.join(); // Wait for all subtasks to complete or fail
scope.throwIfFailed(); // Propagate any exception from subtasks
String result1 = task1.get();
String result2 = task2.get();
System.out.println("Main task completed successfully!");
System.out.println("Result 1: " + result1);
System.out.println("Result 2: " + result2);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Main task interrupted: " + e.getMessage());
} catch (ExecutionException e) {
System.err.println("One or more subtasks failed: " + e.getCause().getMessage());
} finally {
System.out.println("Ending main task at " + System.currentTimeMillis());
}
}
}
```
Output example (Failure scenario, if `System.currentTimeMillis() % 2 != 0` evaluates to true):
```
Starting main task at 1701234567890
Task 1 (Virtual Thread) starting...
Task 2 (Virtual Thread) starting...
One or more subtasks failed: Task 2 failed unexpectedly!
Ending main task at 1701234569123
```
*(Note: In this `ShutdownOnFailure` scenario, as soon as `task2` fails, the scope initiates shutdown, potentially interrupting `task1` before it can complete its `Thread.sleep`.)*
**Small Challenge:**
Modify the code to use `StructuredTaskScope.ShutdownOnSuccess`. How does the output change if one task completes successfully while another is still running or fails later? What are the implications for your application logic?
---
Virtual Threads provide "threads for free," allowing you to write blocking code as if it were non-blocking. Structured Concurrency, especially with `StructuredTaskScope`, ensures that the lifecycle of child tasks is tied to their parent, drastically simplifying error propagation and cancellation.
Here's an example using `StructuredTaskScope.ShutdownOnFailure` to manage two concurrent virtual tasks:
```java
import java.time.Duration;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.TimeoutException;
// Requires --enable-preview --source 21 (or higher) to run
public class Day47StructuredConcurrency {
public static void main(String[] args) throws InterruptedException {
System.out.println("Starting main task at " + System.currentTimeMillis());
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
StructuredTaskScope.Subtask<String> task1 = scope.fork(() -> {
System.out.println("Task 1 (Virtual Thread) starting...");
Thread.sleep(Duration.ofSeconds(3)); // Simulate work
System.out.println("Task 1 completed.");
return "Data from Service A";
});
StructuredTaskScope.Subtask<String> task2 = scope.fork(() -> {
System.out.println("Task 2 (Virtual Thread) starting...");
Thread.sleep(Duration.ofSeconds(1)); // Simulate work
if (System.currentTimeMillis() % 2 != 0) { // Simulate occasional failure
throw new RuntimeException("Task 2 failed unexpectedly!");
}
System.out.println("Task 2 completed.");
return "Data from Service B";
});
scope.join(); // Wait for all subtasks to complete or fail
scope.throwIfFailed(); // Propagate any exception from subtasks
String result1 = task1.get();
String result2 = task2.get();
System.out.println("Main task completed successfully!");
System.out.println("Result 1: " + result1);
System.out.println("Result 2: " + result2);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Main task interrupted: " + e.getMessage());
} catch (ExecutionException e) {
System.err.println("One or more subtasks failed: " + e.getCause().getMessage());
} finally {
System.out.println("Ending main task at " + System.currentTimeMillis());
}
}
}
```
Output example (Failure scenario, if `System.currentTimeMillis() % 2 != 0` evaluates to true):
```
Starting main task at 1701234567890
Task 1 (Virtual Thread) starting...
Task 2 (Virtual Thread) starting...
One or more subtasks failed: Task 2 failed unexpectedly!
Ending main task at 1701234569123
```
*(Note: In this `ShutdownOnFailure` scenario, as soon as `task2` fails, the scope initiates shutdown, potentially interrupting `task1` before it can complete its `Thread.sleep`.)*
**Small Challenge:**
Modify the code to use `StructuredTaskScope.ShutdownOnSuccess`. How does the output change if one task completes successfully while another is still running or fails later? What are the implications for your application logic?
---