Saltar al contenido
ES EN

Guía para migrar a Structured Concurrency final en JDK 28

Pasos prácticos para llevar tu código de StructuredTaskScope a JDK 28 tras la JEP 543: qué cambia desde JDK 27, JDK 25 y JDK 26, y cómo adaptarlo.

Ingeniería de software

Tras siete rondas de preview, la concurrencia estructurada ya es una API final: la JEP 543 quedó integrada en JDK 28 sin cambios de API respecto a JDK 27. En esta guía repasamos cómo migrar según la versión de la que partas, qué forma tiene el código resultante y dónde conviene mirar con más cuidado.

La idea de fondo no ha cambiado desde el incubador de la JEP 428: tratar un grupo de subtareas en hilos distintos como una sola unidad de trabajo. Si una falla, las demás se cancelan, y el ámbito padre no termina hasta que todos los hilos hijos han acabado.

Lo que necesitas

Un proyecto que ya use StructuredTaskScope y un JDK 28 para compilar y probar. Ten a mano la documentación del paquete java.util.concurrent y localiza en tu código los joiners personalizados y los manejadores de excepciones alrededor de join().

Esquema de un ámbito estructurado con un hilo padre que lanza varias subtareas y espera a que todas terminen
El ámbito padre no termina hasta que acaban todas las subtareas.

Pasos de migración

  1. Identifica de qué versión partes: si estás en JDK 27, el cambio es mínimo; si vienes de JDK 25 o 26, hay adaptaciones de código.
  2. En JDK 27: elimina --enable-preview de la compilación y de la ejecución, y recompila contra JDK 28.
  3. En JDK 26: sustituye FailedException por ExecutionException y añade el parámetro de tipo R_X en las firmas de tus joiners.
  4. Revisa los timeouts: StructuredTaskScope.TimeoutException desaparece y el fallo se comunica con una causa CancelledByTimeoutException; además, onTimeout() pasa a ser el método abstracto timeout().
  5. Busca usos de Joiner.awaitAll(), que ya no existe, y sustitúyelos por la política que realmente necesites.
  6. Ejecuta la batería de tests y comprueba especialmente los caminos de error y de cancelación.

Cómo queda el código final

El caso básico abre el ámbito con open(), lanza las subtareas con fork() (cada una en un hilo virtual por defecto) y espera con join(), que lanza ExecutionException si algo falla. Al cerrar el bloque try, se cancelan las subtareas pendientes y se espera a que sus hilos terminen.

Response handle() throws ExecutionException, InterruptedException {
    try (var scope = StructuredTaskScope.open()) {
        Subtask<String> user = scope.fork(() -> findUser());
        Subtask<Integer> order = scope.fork(() -> fetchOrder());
        scope.join(); // lanza ExecutionException si falla alguna
        return new Response(user.get(), order.get());
    }
}

Para fijar un plazo y un nombre de ámbito (útil para observabilidad) se pasa una configuración a open(). Si el plazo vence, la causa de la ExecutionException será CancelledByTimeoutException, y puedes distinguirla con un switch sobre e.getCause().

try (var scope = StructuredTaskScope.open(
        cf -> cf.withTimeout(Duration.ofSeconds(2)).withName("profile-page"))) {
    Subtask<String> user = scope.fork(() -> findUser());
    Subtask<Integer> order = scope.fork(() -> fetchOrder());
    scope.join();
    return new Response(user.get(), order.get());
}
Diagrama de una subtarea que falla y provoca la cancelación del resto de subtareas del ámbito
Un fallo en una subtarea cancela las demás.

Joiners personalizados

El tercer parámetro de tipo, R_X, permite que tu joiner declare su propia excepción y que join() la lance directamente, sin envolverla. Con un joiner de quórum, por ejemplo, join() lanzaría tu QuorumException. Su método timeout() debe lanzar esa misma excepción con CancelledByTimeoutException como causa.

Truco Si dudas qué política usar, empieza por las integradas: Joiner.allSuccessfulOrThrow() devuelve una lista directamente, anySuccessfulOrThrow() devuelve el primer resultado válido y allUntil(Predicate) cancela cuando se cumple una condición. Escribe un joiner propio solo cuando ninguna encaje.

Un último aviso: esta guía se basa en la descripción de la JEP y en el artículo de InfoQ sobre la finalización en JDK 28. La documentación enlazada es de acceso anticipado, así que contrasta los detalles con la versión definitiva de tu JDK antes de dar la migración por cerrada.

Fuente original: infoq.com

Artículo generado mediante AI.larebelion

Kernel

· Dirección de tecnología · España

«Después de siete previews, el mejor movimiento es medir cuánto código tuyo toca excepciones y joiners, y migrar por ahí.»

Comentarios

Publicar un comentario