Saturday, May 03, 2025

Java 24: Structured Concurrency

With Java 24, Structured Concurrency moves closer to becoming a first-class feature in the Java platform. This is currently a preview language feature.

Traditional concurrency in Java often results in fragmented and error-prone code, where related threads are launched independently and can be hard to manage or coordinate. For example, to fetch a user and order in parallel, and then process the results, you would typically use an ExecutorService as shown below:

ExecutorService executor = Executors.newFixedThreadPool(2);
Future<String> userFuture = executor.submit(() -> fetchUser());
Future<String> orderFuture = executor.submit(() -> fetchOrder());
String user = userFuture.get();   // blocks until user is fetched
String order = orderFuture.get(); // blocks until order is fetched
String result = process(user, order);

The downsides of the above approach are:

  • If one task fails, the other continues unless manually cancelled
  • The executor and tasks outlive the method unless explicitly shut down
  • You must manage the executor, handle exceptions, and ensure cleanup

Structured Concurrency abstracts much of this complexity, allowing you to focus on what your code is doing rather than how to coordinate threads. It enforces a hierarchical structure, in which tasks spawned together must complete together, much like local variables within a method.

StructuredTaskScope
Here is an example of using the StructuredTaskScope API:

try (var scope = new StructuredTaskScope<String>()) {
  Subtask<String> userTask = scope.fork(() -> fetchUser());
  Subtask<String> orderTask = scope.fork(() -> fetchOrder());

  scope.join(); // Wait for all subtasks to complete

  String user = userTask.get();
  String order = orderTask.get();

  System.out.println("user: " + user);
  System.out.println("order: " + order);
}

StructuredTaskScope has two subclasses, ShutdownOnSuccess and ShutdownOnFailure, to control how the scope reacts to task completion or failure.

StructuredTaskScope.ShutdownOnFailure
With this policy, if any task fails, the scope cancels the remaining tasks, and propagates the exception when throwIfFailed() is called.

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
  Subtask<String> userTask = scope.fork(() -> fetchUser());
  Subtask<String> orderTask = scope.fork(() -> fetchOrder());

  // wait for all subtasks to complete, or one to fail
  scope.join();
  
  // throw if any subtask failed 
  scope.throwIfFailed();

  String user = userTask.get();
  String order = orderTask.get();

  System.out.println("user: " + user);
  System.out.println("order: " + order);
}

StructuredTaskScope.ShutdownOnSuccess
This policy is the opposite — it stops once one task succeeds, cancelling the others. It's great when you want the first successful result and don't care about the rest.

try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
  scope.fork(() -> fetchFromPrimary());
  scope.fork(() -> fetchFromBackup());

  // wait for any subtask to complete, or all to fail
  scope.join();

  // get the result of the first task that completed successfully,
  // or throw an exception if none did
  System.out.println(scope.result()); 
}

Sunday, March 30, 2025

Java 24: Scoped Values

Java 24 introduces Scoped Values, a powerful alternative to ThreadLocal that offers better performance and cleaner code for managing per-thread data. This is a preview language feature.

Here's an example of ScopedValue in action:

private static final ScopedValue<String> USER_ID = ScopedValue.newInstance();

public void handle(Request req, String userId) {
  ScopedValue.where(USER_ID, userId)
    .run(() -> handle(req));
}

private void handle(Request req) {
  String data = getData(req);
  // Do something else
}

private String getData(Request req) {
  return runQuery(req, USER_ID.get());
}

As shown above, ScopedValue provides a means to pass data (the userId) securely to a faraway method without using method parameters. The faraway method can access the data via the ScopedValue object. This eliminates the need to pass additional parameters explicitly through multiple method calls.

ScopedValue vs ThreadLocal
  • Scoped Values are immutable once set inside ScopedValue.where(...). On the other hand, ThreadLocal allows values to be changed at any time, which can lead to inconsistent state across different parts of a request.
  • Scoped Values are automatically removed after the scope ends, whereas ThreadLocal requires an explicit call to remove() to avoid memory leaks, especially in thread pools.
  • Scoped Values bind data to a specific execution scope, ensuring that when a new task starts on a thread, it doesn’t inherit values from a previous request. ThreadLocal stores data at the thread level, meaning values persist across multiple tasks when using a thread pool.
  • Scoped Values work well with virtual threads and structured concurrency APIs.

For comparison, here's the same example using ThreadLocal:

private static final ThreadLocal<String> USER_ID = new ThreadLocal<>();

public void handle(Request req, String userId) {
  try {
    USER_ID.set(userId);
    handle(req);
  } finally {
    USER_ID.remove(); // to prevent memory leaks
  }
}

private void handle(Request req) {
  String data = getData(req);
  // Do something else
}

private String getData(Request req) {
  return runQuery(req, USER_ID.get());
}

While ThreadLocal still has its uses, most new applications will benefit from Scoped Values’ immutability, automatic cleanup, and better thread management.

Friday, March 28, 2025

Java 24: Primitive Types in Patterns, instanceof, and switch

Java 24 introduces enhancements to pattern matching by allowing primitive types in all pattern contexts, and extending instanceof and switch to work with all primitive types. This is a preview language feature.

Previously, pattern matching for switch only supported reference types such as Integer i, but now it supports primitive types too. For example:

int i = 100;
String s = switch(i) {
  case 1 -> "one";
  case 2 -> "two";
  case int i when i > 2 -> "too big";
  default -> "unsupported";
}

Similarly, instanceof has been enhanced to support primitives, as shown in the example below:

int i = 1;
if (i instanceof byte b) {
  // i has been cast to byte and assigned to b
}
Related posts:
Java 19: Record Patterns
Java 17: Pattern Matching for Switch
Java 14: Pattern Matching for instanceof

Wednesday, January 01, 2025

fahd.blog in 2024

Happy 2025, everyone!

I'd like to wish everyone a great start to an even greater new year!

In keeping with tradition, here's one last look back at fahd.blog in 2024.

During 2024, I posted 10 new entries on fahd.blog. I am also thrilled that I have more readers from all over the world! Thanks for reading and especially for giving feedback.

Top 3 posts of 2024:

I'm going to be writing a lot more this year, so stay tuned for more great techie tips, tricks and hacks! :)

Related posts:

Saturday, August 24, 2024

JavaScript Blobs

A Blob (Binary Large Object) is a data structure used to store raw data. It can be created using the Blob constructor. For instance:

const myBlob = new Blob(['Hello, world!'], { type: 'text/plain' });

You can use Blobs to create URLs, which can be directly embedded into HTML documents. For example, you can create a Blob containing text data and then generate a download link for it. When the user clicks this link, they can download the Blob content as a file. This is shown below:

<html>
<head/>
<body>
  <h1>Download Blob Example</h1>

  <script>
    const createDownloadLink = (content, filename) => {
      // Create a Blob from the content
      const blob = new Blob([content], { type: 'text/plain' });
      
      // Create a URL for the Blob
      const url = URL.createObjectURL(blob);
      
      // Create an <a> element
      const a = document.createElement('a');
      a.href = url;
      a.download = filename;
      a.textContent = `Download ${filename}`;

      // append to body
      document.body.appendChild(a);
      
      // revoke URL after some time or on user action
      // URL.revokeObjectURL(url); 
    }
    createDownloadLink('some content', 'example.txt');
  </script>
</body>
</html>

You can also use a Blob to dynamically generate code and create JavaScript files on-the-fly! Here’s an example of how to create a Web Worker from a Blob:

<html>
<head/>
<body>
  <h1>Web Worker Blob Example</h1>
  <p id="result"></p>

  <script>
    const workerScript = `
      onmessage = e => {
        postMessage(e.data * 2);
      };
    `;

    const blob = new Blob([workerScript], { type: 'application/javascript' });
    const url = URL.createObjectURL(blob);
    const worker = new Worker(url);

    worker.onmessage = e => {
      document.getElementById('result').textContent = 'Worker result: ' + e.data;
      URL.revokeObjectURL(url); // Clean up Blob URL
    };

    worker.postMessage('2');
  </script>
</body>
</html>