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?
---
Here's your Day 48 Java learning series post!
Hey Java Devs! ๐ Day 48 is here, and we're diving deep into crafting truly resilient and expressive domain models! Forget simple POJOs; today we explore how modern Java features like `record` types, `sealed classes`/`interfaces`, and `pattern matching` elevate immutability. Learn to build domain objects that are not only safe and predictable but also incredibly clear and compiler-verified. Let's make our domain models rock-solid! ๐งฑโจ
Let's model different payment methods using these powerful features, ensuring our code is both expressive and robust:
```java
// Define a sealed interface for payment methods
// This restricts which classes can implement it, providing compile-time control.
sealed interface PaymentMethod permits CreditCardPayment, PayPalPayment, BankTransferPayment {
String description(); // Common behavior for all payment methods
}
// Record for Credit Card Payment - concise, immutable data carrier
record CreditCardPayment(String cardNumber, String expiryDate, String cvv) implements PaymentMethod {
@Override
public String description() {
return "Credit Card (ends in " + cardNumber.substring(cardNumber.length() - 4) + ")";
}
}
// Record for PayPal Payment
record PayPalPayment(String email) implements PaymentMethod {
@Override
public String description() {
return "PayPal (" + email + ")";
}
}
// Record for Bank Transfer Payment
record BankTransferPayment(String bankName, String accountNumber) implements PaymentMethod {
@Override
public String description() {
return "Bank Transfer (" + bankName + ")";
}
}
public class DomainModelDemo {
// Using pattern matching with switch expressions for exhaustive handling
public static String processPayment(PaymentMethod method) {
return switch (method) {
case CreditCardPayment cc -> "Processing credit card payment: " + cc.description();
case PayPalPayment pp -> "Processing PayPal payment: " + pp.description();
case BankTransferPayment bt -> "Processing bank transfer payment: " + bt.description();
// The compiler will warn you if you don't handle all permitted types!
};
}
public static void main(String[] args) {
PaymentMethod card = new CreditCardPayment("1234-5678-9012-3456", "12/25", "123");
PaymentMethod paypal = new PayPalPayment("user@example.com");
PaymentMethod bank = new BankTransferPayment("AwesomeBank", "987654321");
System.out.println(processPayment(card));
System.out.println(processPayment(paypal));
System.out.println(processPayment(bank));
}
}
```
Output example:
```
Processing credit card payment: Credit Card (ends in 3456)
Processing PayPal payment: PayPal (user@example.com)
Processing bank transfer payment: Bank Transfer (AwesomeBank)
```
๐ก **Your Turn!** Try adding a new payment method, say `CryptocurrencyPayment`, to our `sealed interface`. What happens if you forget to add a `case` for it in the `processPayment` switch expression? See how the compiler guides you towards exhaustive handling! ๐ค This is the power of resilient domain modeling!
---
Hey Java Devs! ๐ Day 48 is here, and we're diving deep into crafting truly resilient and expressive domain models! Forget simple POJOs; today we explore how modern Java features like `record` types, `sealed classes`/`interfaces`, and `pattern matching` elevate immutability. Learn to build domain objects that are not only safe and predictable but also incredibly clear and compiler-verified. Let's make our domain models rock-solid! ๐งฑโจ
Let's model different payment methods using these powerful features, ensuring our code is both expressive and robust:
```java
// Define a sealed interface for payment methods
// This restricts which classes can implement it, providing compile-time control.
sealed interface PaymentMethod permits CreditCardPayment, PayPalPayment, BankTransferPayment {
String description(); // Common behavior for all payment methods
}
// Record for Credit Card Payment - concise, immutable data carrier
record CreditCardPayment(String cardNumber, String expiryDate, String cvv) implements PaymentMethod {
@Override
public String description() {
return "Credit Card (ends in " + cardNumber.substring(cardNumber.length() - 4) + ")";
}
}
// Record for PayPal Payment
record PayPalPayment(String email) implements PaymentMethod {
@Override
public String description() {
return "PayPal (" + email + ")";
}
}
// Record for Bank Transfer Payment
record BankTransferPayment(String bankName, String accountNumber) implements PaymentMethod {
@Override
public String description() {
return "Bank Transfer (" + bankName + ")";
}
}
public class DomainModelDemo {
// Using pattern matching with switch expressions for exhaustive handling
public static String processPayment(PaymentMethod method) {
return switch (method) {
case CreditCardPayment cc -> "Processing credit card payment: " + cc.description();
case PayPalPayment pp -> "Processing PayPal payment: " + pp.description();
case BankTransferPayment bt -> "Processing bank transfer payment: " + bt.description();
// The compiler will warn you if you don't handle all permitted types!
};
}
public static void main(String[] args) {
PaymentMethod card = new CreditCardPayment("1234-5678-9012-3456", "12/25", "123");
PaymentMethod paypal = new PayPalPayment("user@example.com");
PaymentMethod bank = new BankTransferPayment("AwesomeBank", "987654321");
System.out.println(processPayment(card));
System.out.println(processPayment(paypal));
System.out.println(processPayment(bank));
}
}
```
Output example:
```
Processing credit card payment: Credit Card (ends in 3456)
Processing PayPal payment: PayPal (user@example.com)
Processing bank transfer payment: Bank Transfer (AwesomeBank)
```
๐ก **Your Turn!** Try adding a new payment method, say `CryptocurrencyPayment`, to our `sealed interface`. What happens if you forget to add a `case` for it in the `processPayment` switch expression? See how the compiler guides you towards exhaustive handling! ๐ค This is the power of resilient domain modeling!
---
๐ **Day 49: Diving Deep into Modern Java Concurrency!** ๐
Hello Java Enthusiasts! Today, we're unlocking the full potential of **Project Loom** in Java. Forget the limitations of traditional threads โ we're talking about architecting systems capable of handling *millions* of concurrent virtual threads with ease! ๐คฏ Get ready to explore how Virtual Threads redefine scalability and simplify highly concurrent programming. No more worrying about thread pool sizes or context switching overhead as much. Let's see them in action! ๐
```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 VirtualThreadMillions {
private static final int NUMBER_OF_TASKS = 500_000; // Let's aim high but not *too* high for quick demo
public static void main(String[] args) throws InterruptedException {
System.out.println("๐ฅ Starting " + NUMBER_OF_TASKS + " virtual threads...");
Instant start = Instant.now();
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, NUMBER_OF_TASKS).forEach(i -> {
executor.submit(() -> {
try {
// Simulate some non-CPU bound work, like an I/O operation
Thread.sleep(Duration.ofMillis(1));
// In a real app, this would be an actual blocking call
// System.out.println("Task " + i + " finished by " + Thread.currentThread()); // Too much output
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
});
} // ExecutorService is automatically shut down here
Instant end = Instant.now();
System.out.println("โ All " + NUMBER_OF_TASKS + " virtual threads completed in " + Duration.between(start, end).toMillis() + " ms.");
System.out.println("๐ Scalability just got a massive boost!");
}
}
```
**Output example:**
```
๐ฅ Starting 500000 virtual threads...
โ All 500000 virtual threads completed in 568 ms.
๐ Scalability just got a massive boost!
```
**Small challenge:**
Modify the `NUMBER_OF_TASKS` to 1 million or even 5 million. How does the execution time change? Now, try to achieve the same with `Executors.newFixedThreadPool(200)` (using platform threads) for simulating I/O-bound tasks. What differences do you observe in performance and resource usage (e.g., memory)? Share your findings! ๐
---
Hello Java Enthusiasts! Today, we're unlocking the full potential of **Project Loom** in Java. Forget the limitations of traditional threads โ we're talking about architecting systems capable of handling *millions* of concurrent virtual threads with ease! ๐คฏ Get ready to explore how Virtual Threads redefine scalability and simplify highly concurrent programming. No more worrying about thread pool sizes or context switching overhead as much. Let's see them in action! ๐
```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 VirtualThreadMillions {
private static final int NUMBER_OF_TASKS = 500_000; // Let's aim high but not *too* high for quick demo
public static void main(String[] args) throws InterruptedException {
System.out.println("๐ฅ Starting " + NUMBER_OF_TASKS + " virtual threads...");
Instant start = Instant.now();
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, NUMBER_OF_TASKS).forEach(i -> {
executor.submit(() -> {
try {
// Simulate some non-CPU bound work, like an I/O operation
Thread.sleep(Duration.ofMillis(1));
// In a real app, this would be an actual blocking call
// System.out.println("Task " + i + " finished by " + Thread.currentThread()); // Too much output
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
});
} // ExecutorService is automatically shut down here
Instant end = Instant.now();
System.out.println("โ All " + NUMBER_OF_TASKS + " virtual threads completed in " + Duration.between(start, end).toMillis() + " ms.");
System.out.println("๐ Scalability just got a massive boost!");
}
}
```
**Output example:**
```
๐ฅ Starting 500000 virtual threads...
โ All 500000 virtual threads completed in 568 ms.
๐ Scalability just got a massive boost!
```
**Small challenge:**
Modify the `NUMBER_OF_TASKS` to 1 million or even 5 million. How does the execution time change? Now, try to achieve the same with `Executors.newFixedThreadPool(200)` (using platform threads) for simulating I/O-bound tasks. What differences do you observe in performance and resource usage (e.g., memory)? Share your findings! ๐
---
๐ **Day 50: Beyond the JIT - Deep Dive into GraalVM Native Images!** ๐
Congratulations on reaching Day 50! Today, we're not just running Java; we're launching it into a new era! ๐ Forget traditional JVM startup times. We're exploring GraalVM Native Images, a revolutionary way to compile Java applications into standalone executables. This means incredibly fast startup, tiny memory footprints, and deployable binaries without needing a JVM installed. It's Java, but faster and lighter than ever! Get ready to see how Ahead-of-Time (AOT) compilation transforms your applications.
```java
// Day50NativeApp.java
public class Day50NativeApp {
public static void main(String[] args) {
System.out.println("๐ Hello from Day 50's Native Image!");
System.out.printf("Arguments received: %d%n", args.length);
if (args.length > 0) {
System.out.println("First argument: " + args[0]);
}
}
}
```
To compile and run this as a native image (assuming GraalVM `native-image` tool is installed):
1. `javac Day50NativeApp.java`
2. `native-image --no-fallback -jar Day50NativeApp.jar Day50NativeApp` (This command assumes you've packaged it or use the class name directly)
* *Simpler for this example:* `native-image -cp . Day50NativeApp`
3. `./day50nativeapp`
Output example:
```
๐ Hello from Day 50's Native Image!
Arguments received: 0
```
Small challenge:
Install GraalVM (if you haven't already!) and compile the `Day50NativeApp.java` file into a native executable. Compare its startup time (using `time ./day50nativeapp`) with running it traditionally via `java -cp . Day50NativeApp`. You'll be amazed by the difference! Share your results in the comments! โก
---
Congratulations on reaching Day 50! Today, we're not just running Java; we're launching it into a new era! ๐ Forget traditional JVM startup times. We're exploring GraalVM Native Images, a revolutionary way to compile Java applications into standalone executables. This means incredibly fast startup, tiny memory footprints, and deployable binaries without needing a JVM installed. It's Java, but faster and lighter than ever! Get ready to see how Ahead-of-Time (AOT) compilation transforms your applications.
```java
// Day50NativeApp.java
public class Day50NativeApp {
public static void main(String[] args) {
System.out.println("๐ Hello from Day 50's Native Image!");
System.out.printf("Arguments received: %d%n", args.length);
if (args.length > 0) {
System.out.println("First argument: " + args[0]);
}
}
}
```
To compile and run this as a native image (assuming GraalVM `native-image` tool is installed):
1. `javac Day50NativeApp.java`
2. `native-image --no-fallback -jar Day50NativeApp.jar Day50NativeApp` (This command assumes you've packaged it or use the class name directly)
* *Simpler for this example:* `native-image -cp . Day50NativeApp`
3. `./day50nativeapp`
Output example:
```
๐ Hello from Day 50's Native Image!
Arguments received: 0
```
Small challenge:
Install GraalVM (if you haven't already!) and compile the `Day50NativeApp.java` file into a native executable. Compare its startup time (using `time ./day50nativeapp`) with running it traditionally via `java -cp . Day50NativeApp`. You'll be amazed by the difference! Share your results in the comments! โก
---
What is a primary advantage of GraalVM Native Images over traditional JVM execution for a Java application?
Anonymous Quiz
100%
A. It allows for dynamic bytecode generation at runtime, enhancing flexibility.
0%
B. It significantly reduces memory footprint and provides near-instant startup times.
0%
C. It enables hot-swapping of classes in production without application restarts.
0%
D. It improves the peak performance of long-running applications through advanced JIT optimizations.
๐ Day 52: The Loom Revolution! ๐งตโจ
Get ready to transform your understanding of Java concurrency! Today, we're diving deep into **Project Loom** and its game-changing **Virtual Threads**. This isn't just an update; it's a fundamental shift, enabling you to architect applications for *millions* of concurrent users with unprecedented ease and efficiency. Say goodbye to the limitations of traditional threads and embrace a future of lightweight, high-throughput concurrency!
```java
import java.time.Duration;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.stream.IntStream;
public class VirtualThreadDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("๐ Day 52: Virtual Threads in Action!");
long startTime = System.currentTimeMillis();
// Create an ExecutorService that spawns a new virtual thread for each task
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 5000).forEach(i -> { // Let's launch 5000 virtual threads!
executor.submit(() -> {
try {
// Simulate a short, non-CPU-intensive blocking operation (e.g., I/O call)
Thread.sleep(Duration.ofMillis(5));
// Uncomment to see thread names, notice many 'VirtualThread'
// System.out.println("Task " + i + " completed on: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
});
} // The ExecutorService is automatically shut down here, waiting for all submitted tasks to complete.
long endTime = System.currentTimeMillis();
System.out.println(String.format("โ %d virtual threads completed their tasks in %d ms.", 5000, (endTime - startTime)));
System.out.println("Main thread continues. Current thread: " + Thread.currentThread().getName());
}
}
```
Output example:
```
๐ Day 52: Virtual Threads in Action!
โ 5000 virtual threads completed their tasks in 35 ms. (Actual time may vary based on system)
Main thread continues. Current thread: main
```
**Challenge:**
Modify the code to launch 100,000 virtual threads. Observe the execution time and compare it with trying to launch 100,000 traditional platform threads (be cautious when attempting with platform threads, as this might consume significant memory/OS resources on your system!). How does Project Loom enable such massive concurrency?
---
Get ready to transform your understanding of Java concurrency! Today, we're diving deep into **Project Loom** and its game-changing **Virtual Threads**. This isn't just an update; it's a fundamental shift, enabling you to architect applications for *millions* of concurrent users with unprecedented ease and efficiency. Say goodbye to the limitations of traditional threads and embrace a future of lightweight, high-throughput concurrency!
```java
import java.time.Duration;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.stream.IntStream;
public class VirtualThreadDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("๐ Day 52: Virtual Threads in Action!");
long startTime = System.currentTimeMillis();
// Create an ExecutorService that spawns a new virtual thread for each task
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 5000).forEach(i -> { // Let's launch 5000 virtual threads!
executor.submit(() -> {
try {
// Simulate a short, non-CPU-intensive blocking operation (e.g., I/O call)
Thread.sleep(Duration.ofMillis(5));
// Uncomment to see thread names, notice many 'VirtualThread'
// System.out.println("Task " + i + " completed on: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
});
} // The ExecutorService is automatically shut down here, waiting for all submitted tasks to complete.
long endTime = System.currentTimeMillis();
System.out.println(String.format("โ %d virtual threads completed their tasks in %d ms.", 5000, (endTime - startTime)));
System.out.println("Main thread continues. Current thread: " + Thread.currentThread().getName());
}
}
```
Output example:
```
๐ Day 52: Virtual Threads in Action!
โ 5000 virtual threads completed their tasks in 35 ms. (Actual time may vary based on system)
Main thread continues. Current thread: main
```
**Challenge:**
Modify the code to launch 100,000 virtual threads. Observe the execution time and compare it with trying to launch 100,000 traditional platform threads (be cautious when attempting with platform threads, as this might consume significant memory/OS resources on your system!). How does Project Loom enable such massive concurrency?
---
๐ Day 53: Level up your Java architecture! ๐ฐ We're diving into **Type-Safe Kingdoms** with Java's powerful **Sealed Types** and **Exhaustive Pattern Matching**. These modern features let you sculpt incredibly robust APIs, ensuring compile-time guarantees for your domain models. No more runtime surprises! Learn to define a hierarchy of types where you know *exactly* what's possible, making your code safer, cleaner, and easier to refactor. Get ready to build more resilient systems! โจ
```java
public class TypeSafeKingdoms {
// 1. Define a sealed interface: Shape can only be implemented by permitted types
sealed interface Shape permits Circle, Rectangle, Triangle {
double area(); // All shapes must implement an area calculation
}
// 2. Implementations as records: concise and immutable data carriers
record Circle(double radius) implements Shape {
@Override
public double area() {
return Math.PI * radius * radius;
}
}
record Rectangle(double width, double height) implements Shape {
@Override
public double area() {
return width * height;
}
}
record Triangle(double base, double height) implements Shape {
@Override
public double area() {
return 0.5 * base * height;
}
}
// 3. Method using Pattern Matching for switch expressions (Java 21+)
public static String describeShape(Shape shape) {
// This switch expression is exhaustive: the compiler ensures all
// permitted sub-types of Shape (Circle, Rectangle, Triangle) are handled.
// If you remove a 'case', the compiler will flag it as a non-exhaustive switch!
return switch (shape) {
case Circle c -> "A circle with radius " + c.radius() + ", area: " + c.area();
case Rectangle r -> "A rectangle with width " + r.width() + " and height " + r.height() + ", area: " + r.area();
case Triangle t -> "A triangle with base " + t.base() + " and height " + t.height() + ", area: " + t.area();
// No 'default' case is needed here because the switch is exhaustive
// due to the nature of sealed types and pattern matching.
};
}
public static void main(String[] args) {
Shape circle = new Circle(5.0);
Shape rectangle = new Rectangle(4.0, 6.0);
Shape triangle = new Triangle(3.0, 4.0);
System.out.println(describeShape(circle));
System.out.println(describeShape(rectangle));
System.out.println(describeShape(triangle));
}
}
```
Output example:
```
A circle with radius 5.0, area: 78.53981633974483
A rectangle with width 4.0 and height 6.0, area: 24.0
A triangle with base 3.0 and height 4.0, area: 6.0
```
**Small challenge:**
How would you extend the `Shape` hierarchy to include a `Square`? Would you make `Square` directly implement `Shape` or extend `Rectangle`? What are the implications for your `describeShape` method and the `permits` clause of `Shape`? Share your thoughts! ๐
---
```java
public class TypeSafeKingdoms {
// 1. Define a sealed interface: Shape can only be implemented by permitted types
sealed interface Shape permits Circle, Rectangle, Triangle {
double area(); // All shapes must implement an area calculation
}
// 2. Implementations as records: concise and immutable data carriers
record Circle(double radius) implements Shape {
@Override
public double area() {
return Math.PI * radius * radius;
}
}
record Rectangle(double width, double height) implements Shape {
@Override
public double area() {
return width * height;
}
}
record Triangle(double base, double height) implements Shape {
@Override
public double area() {
return 0.5 * base * height;
}
}
// 3. Method using Pattern Matching for switch expressions (Java 21+)
public static String describeShape(Shape shape) {
// This switch expression is exhaustive: the compiler ensures all
// permitted sub-types of Shape (Circle, Rectangle, Triangle) are handled.
// If you remove a 'case', the compiler will flag it as a non-exhaustive switch!
return switch (shape) {
case Circle c -> "A circle with radius " + c.radius() + ", area: " + c.area();
case Rectangle r -> "A rectangle with width " + r.width() + " and height " + r.height() + ", area: " + r.area();
case Triangle t -> "A triangle with base " + t.base() + " and height " + t.height() + ", area: " + t.area();
// No 'default' case is needed here because the switch is exhaustive
// due to the nature of sealed types and pattern matching.
};
}
public static void main(String[] args) {
Shape circle = new Circle(5.0);
Shape rectangle = new Rectangle(4.0, 6.0);
Shape triangle = new Triangle(3.0, 4.0);
System.out.println(describeShape(circle));
System.out.println(describeShape(rectangle));
System.out.println(describeShape(triangle));
}
}
```
Output example:
```
A circle with radius 5.0, area: 78.53981633974483
A rectangle with width 4.0 and height 6.0, area: 24.0
A triangle with base 3.0 and height 4.0, area: 6.0
```
**Small challenge:**
How would you extend the `Shape` hierarchy to include a `Square`? Would you make `Square` directly implement `Shape` or extend `Rectangle`? What are the implications for your `describeShape` method and the `permits` clause of `Shape`? Share your thoughts! ๐
---
๐ Day 56: Dive into the future of high-performance Java! ๐ฅ Today we explore **Project Panama's Foreign Memory API (FFM API)**. Say goodbye to the complexities and dangers of JNI and `sun.misc.Unsafe`! The FFM API offers a *safe, efficient, and idiomatic* way to interact with off-heap memory and native code, unlocking incredible performance gains for data-intensive applications. Get ready to manage memory outside the JVM heap with native precision!
```java
import java.lang.foreign.*; // This API requires Java 19+ (incubating) or Java 22+ (stable)
// For older Java versions (19-21), you might need --enable-preview
public class PanamaMemoryDemo {
public static void main(String[] args) {
// Allocate a memory segment off-heap to hold 5 integers.
// Arena.ofConfined() creates a confined arena, which manages the lifecycle
// of allocated memory. It's automatically closed (and memory deallocated)
// at the end of the try-with-resources block.
try (Arena arena = Arena.ofConfined()) {
// Allocate space for 5 integers (each JAVA_INT is 4 bytes).
// The total size will be 5 * 4 = 20 bytes.
MemorySegment segment = arena.allocate(ValueLayout.JAVA_INT, 5);
System.out.println("Writing values to off-heap memory:");
// Write values to the off-heap segment
for (int i = 0; i < 5; i++) {
int valueToWrite = i * 100;
// setAtIndex writes an int at the given logical index (not byte offset)
segment.setAtIndex(ValueLayout.JAVA_INT, i, valueToWrite);
System.out.println("Wrote " + valueToWrite + " at index " + i);
}
System.out.println("nReading values from off-heap memory:");
// Read values back from the off-heap segment
for (int i = 0; i < 5; i++) {
// getAtIndex reads an int from the given logical index
int value = segment.getAtIndex(ValueLayout.JAVA_INT, i);
System.out.println("Index " + i + ": " + value);
}
} // Arena is closed here, automatically deallocating the 'segment' memory.
}
}
```
Output example:
```
Writing values to off-heap memory:
Wrote 0 at index 0
Wrote 100 at index 1
Wrote 200 at index 2
Wrote 300 at index 3
Wrote 400 at index 4
Reading values from off-heap memory:
Index 0: 0
Index 1: 100
Index 2: 200
Index 3: 300
Index 4: 400
```
Small Challenge:
Your Challenge: Modify the code to allocate a `MemorySegment` for 10 `long` values and populate it with their squares (0, 1, 4, 9, ...), then print them out! Remember to use `ValueLayout.JAVA_LONG`. ๐ก
```java
import java.lang.foreign.*; // This API requires Java 19+ (incubating) or Java 22+ (stable)
// For older Java versions (19-21), you might need --enable-preview
public class PanamaMemoryDemo {
public static void main(String[] args) {
// Allocate a memory segment off-heap to hold 5 integers.
// Arena.ofConfined() creates a confined arena, which manages the lifecycle
// of allocated memory. It's automatically closed (and memory deallocated)
// at the end of the try-with-resources block.
try (Arena arena = Arena.ofConfined()) {
// Allocate space for 5 integers (each JAVA_INT is 4 bytes).
// The total size will be 5 * 4 = 20 bytes.
MemorySegment segment = arena.allocate(ValueLayout.JAVA_INT, 5);
System.out.println("Writing values to off-heap memory:");
// Write values to the off-heap segment
for (int i = 0; i < 5; i++) {
int valueToWrite = i * 100;
// setAtIndex writes an int at the given logical index (not byte offset)
segment.setAtIndex(ValueLayout.JAVA_INT, i, valueToWrite);
System.out.println("Wrote " + valueToWrite + " at index " + i);
}
System.out.println("nReading values from off-heap memory:");
// Read values back from the off-heap segment
for (int i = 0; i < 5; i++) {
// getAtIndex reads an int from the given logical index
int value = segment.getAtIndex(ValueLayout.JAVA_INT, i);
System.out.println("Index " + i + ": " + value);
}
} // Arena is closed here, automatically deallocating the 'segment' memory.
}
}
```
Output example:
```
Writing values to off-heap memory:
Wrote 0 at index 0
Wrote 100 at index 1
Wrote 200 at index 2
Wrote 300 at index 3
Wrote 400 at index 4
Reading values from off-heap memory:
Index 0: 0
Index 1: 100
Index 2: 200
Index 3: 300
Index 4: 400
```
Small Challenge:
Your Challenge: Modify the code to allocate a `MemorySegment` for 10 `long` values and populate it with their squares (0, 1, 4, 9, ...), then print them out! Remember to use `ValueLayout.JAVA_LONG`. ๐ก
Which of the following is a primary advantage of using Project Panama's Foreign Memory API (FFM API) over traditional JNI for native memory access?
Anonymous Quiz
50%
It provides a safer and more idiomatic Java way to access off-heap memory, reducing error proneness.
50%
It automatically garbage collects native memory segments without explicit handling.
0%
It allows direct execution of C/C++ code within the JVM without any marshalling.
0%
It only supports memory allocation for primitive types, making it simpler.