Configure virtual threads in Cloud Client Libraries for Java

Virtual threads, made generally available in JDK 21, simplify the development of high concurrency applications. Some key benefits may include:

  • Higher throughput, especially in applications with blocking I/O operations.
  • Memory optimization due to their lightweight nature.
  • Simple concurrency model where the applications can be represented in a thread-per-request style.

Before applying virtual threads to your application, see the Open JDK documentation's JEP 444: Virtual Threads for a detailed description on both the benefits and limitations of this feature.

This guide walks through how to configure Cloud Client Libraries for Java in the googleapis/google-cloud-java repository to use virtual threads.

Prerequisites

Follow these prerequisite steps to use virtual threads with the Cloud Client Libraries for Java that you want to use in your application.

  1. If you have not already, create a Google Cloud Project. See Creating and managing projects.
  2. Install the Google Cloud CLI, which lets you run the sample with your project's credentials.
  3. Log in with Application Default Credentials using the following command. For further instructions, see Set up Application Default Credentials :

    gcloud auth application-default login
    
  4. To make sure Java Development Kit (JDK) 21 or later is installed, run the java -version command and verify that you see the following output. For steps to install JDK, see Setting Up a Java Development Environment.

    openjdk version "21.0.2" 2024-01-16 LTS
    OpenJDK Runtime Environment Temurin-21.0.2+13 (build 21.0.2+13-LTS)
    OpenJDK 64-Bit Server VM Temurin-21.0.2+13 (build 21.0.2+13-LTS, mixed mode, sharing)
    

Import the library into your project

This example uses google-cloud-translate. For steps to use the library in Maven or Gradle, see Use the Cloud Translation for Java

Use the virtual thread executor in the client libraries

When you instantiate a client library class, you can choose its transport implementation (gRPC and REST).

REST transport

The client libraries that implement REST transport use blocking network I/O calls, which benefit from the throughput improvements provided by virtual threads. The following code snippet demonstrates how blocking network I/O traffic in the Cloud Client Libraries for Java can be managed by virtual threads. You can implement this in your own application by overriding the default executor with a virtual thread executor in a TransportChannelProvider instance.

import com.google.cloud.translate.*;
import java.util.concurrent.Executors;

ExecutorService virtualThreadExecutor = Executors.newVirtualThreadPerTaskExecutor();
InstantiatingHttpJsonChannelProvider httpJsonChannelProvider =
        TranslationServiceSettings.defaultHttpJsonTransportProviderBuilder()
            // Use virtual threads for network I/O calls.
            .setExecutor(virtualThreadExecutor)
            .build();

try (TranslationServiceClient client =
    TranslationServiceClient.create(
        TranslationServiceSettings.newHttpJsonBuilder()
            .setTransportChannelProvider(httpJsonChannelProvider)
            .build())) {
  // Make translate requests
}

gRPC transport

Since gRPC already uses asynchronous logic to handle requests, throughput improvements from virtual threads may be less pronounced. However, they can still provide some memory optimization benefits due to their lightweight nature. The following code snippet demonstrates how gRPC transport operations can be managed by virtual threads. You can implement this in your own application by overriding the default executor with a virtual thread executor in a TransportChannelProvider instance.

import com.google.cloud.translate.*;
import java.util.concurrent.Executors;

ExecutorService virtualThreadExecutor = Executors.newVirtualThreadPerTaskExecutor();
InstantiatingGrpcChannelProvider grpcChannelProvider =
        TranslationServiceSettings.defaultGrpcTransportProviderBuilder()
            // Use virtual threads for gRPC transport operations.
            .setExecutor(virtualThreadExecutor)
            .build();

try (TranslationServiceClient client =
        TranslationServiceClient.create(
            TranslationServiceSettings.newBuilder()
                .setTransportChannelProvider(grpcChannelProvider)
                .build())) {
  // Make translate requests
}