JDK 25 traz a quinta prévia do Structured Concurrency, não a versão estável
Entenda o que muda na quinta prévia da API StructuredTaskScope e por que ainda não é hora de usar `--enable-preview` em produção.

O que o JEP 505 entrega (e o que não entrega)
Nota da redação: o texto original afirmava que o campo "Status" do documento oficial do JEP 505 traz o valor "Fifth Preview". Na verdade, o campo Status do JEP 505 é "Closed / Delivered"; "Fifth Preview" é parte do título/nome do JEP ("Structured Concurrency (Fifth Preview)"), não um valor do campo Status. A tese central do artigo, de que o recurso segue em prévia e exige
--enable-preview, permanece correta e é confirmada por outros elementos do próprio JEP (necessidade das flags de preview, JEP 525 já programado como sexta prévia), mas o leitor deve notar essa imprecisão terminológica específica.
O JDK 25 soma a quinta rodada de prévia do Structured Concurrency, com mudanças na forma de abrir escopos e um JEP 525 já programado para a sexta. Para quem mantém ExecutorService manual em produção, entender o modelo já vale a pena, mesmo sem usar a API ainda.
O JEP 505, assinado por Alan Bateman, Viktor Klang e Ron Pressler, descreve o Structured Concurrency na JDK 25, identificada como Release 25 no documento oficial da OpenJDK. O status no documento oficial da OpenJDK é "Fifth Preview": é a quinta vez que a API passa por prévia, não a formalização em API estável. O próprio JEP já aponta o próximo passo, o JEP 525, batizado "Structured Concurrency (Sixth Preview)", mirando a versão seguinte do JDK.

Isso importa porque, para quem mantém serviço Java↳Java42 conteúdosNovidades do Java 26 (para desenvolvedores)Dev (Back & Front) · mar 2026Visual Studio Code para Java: o guia completo (dicas, configuração e extensões)Dev (Back & Front) · out 2025Quarkus: Modernizando a linguagem Java para a era da nuvemDev (Back & Front) · nov 2020Ver tudo em Dev (Back & Front) → em produção, "preview" costuma significar "ainda não mexo nisso". E continua sendo o caso aqui: usar StructuredTaskScope na JDK 25 exige compilar com javac --release 25 --enable-preview Main.java e rodar com java --enable-preview Main. Sem essas flags a API nem compila. O recurso amadureceu desde a incubação no JEP 428 (JDK 19) e passou por seis JDKs até aqui, mas ainda não virou contrato estável que sobrevive a um upgrade de versão sem revisão de código.
Simplificar a programação concorrente introduzindo uma API para concorrência estruturada.
Simplify concurrent programming by introducing an API for structured concurrency.JEP 505, OpenJDK
Da bagunça do ExecutorService ao escopo estruturado
O problema que o JEP descreve é conhecido de quem já escreveu um handle() que dispara duas chamadas de I/O em paralelo com ExecutorService. No padrão atual, cada subtarefa vira um Future independente, e o código principal faz .get() em cada um para esperar o resultado. O JEP lista três formas de isso dar errado em produção.
- Se
findUser()falha, o método lança exceção ao chamaruser.get(), masfetchOrder()continua rodando em sua própria thread: um vazamento de thread. - Se a thread que executa
handle()é interrompida, a interrupção não se propaga às subtarefas, que seguem rodando mesmo depois quehandle()já falhou. - Se
findUser()demora efetchOrder()falha antes,handle()esperafindUser()terminar sem necessidade, em vez de cancelá-lo assim que o erro defetchOrder()aparece.
O ponto central do JEP é que esse tipo de bug nasce porque ExecutorService e Future permitem concorrência irrestrita: qualquer thread pode submeter trabalho a um executor e qualquer outra thread, sem relação com a primeira, pode chamar .get() para aguardar o resultado. Não existe uma estrutura de pai e filho entre as tarefas, então o runtime não tem como propagar cancelamento ou erro automaticamente. Corrigir isso manualmente, com try/finally chamando cancel() nos futures dos outros, funciona, mas é fácil de errar e difícil de revisar em code review.
O que muda na quinta prévia
A API central continua sendo StructuredTaskScope, no pacote java.util.concurrent. Mas a forma de abrir um escopo mudou: nas prévias anteriores (JDK 21 a 24) o escopo nascia de um construtor público, geralmente de uma subclasse de política pronta. No JEP 505, construtores públicos saem de cena e entram métodos estáticos de fábrica.
public static <T> StructuredTaskScope<T, Void> open();
public static <T, R> StructuredTaskScope<T, R> open(
Joiner<? super T, ? extends R> joiner);O open() sem parâmetros cobre o caso comum: espera todas as subtarefas terminarem com sucesso, ou falha assim que uma delas falha. Para outras políticas de conclusão, a prévia introduz o conceito de Joiner, passado para a versão de um parâmetro de open(). É aqui que mora a maior diferença de código entre quem já tinha testado uma prévia anterior do Structured Concurrency e quem for experimentar agora: o esqueleto do try-with-resources é parecido, mas a linha de abertura do escopo muda.
Response handle() throws InterruptedException {
try (var scope = StructuredTaskScope.open()) {
Subtask<String> user = scope.fork(() -> findUser());
Subtask<Integer> order = scope.fork(() -> fetchOrder());
scope.join(); // propaga exceções das subtarefas
return new Response(user.get(), order.get());
}
}Repare que o tipo de retorno de fork() não é mais Future, e sim Subtask, uma mudança que já tinha acontecido no JEP 453 (JDK 21) e se mantém. A chamada scope.join() espera todas as subtarefas como unidade; só depois dela é seguro chamar Subtask::get().
Cancelamento e propagação de erro, na prática
O ganho prático do modelo estruturado é que o ciclo de vida das threads filhas fica preso ao bloco léxico do try-with-resources. Isso resolve, por construção, os três problemas que o ExecutorService manual deixava em aberto.
| Aspecto | ExecutorService manual | StructuredTaskScope (JEP 505) |
|---|---|---|
| Cancelamento ao falhar | Manual, via cancel() em cada future | Automático: falha de uma subtarefa cancela as irmãs |
| Propagação de interrupção | Não se propaga às subtarefas | Propaga ao sair do escopo |
| Escopo de vida das threads | Indefinido, threads podem vazar | Confinado ao bloco try |
| Observabilidade↳Observabilidade11 conteúdosObservabilidade para APIs: os desafios e benefícios dessa abordagemDev (Back & Front) · jan 2025Falhas em Observabilidade afetam os Apps e a Segurança das OrganizaçõesDev (Back & Front) · nov 2023ADK Java 1.0: O Google quer que você pare de gambiarra Python no seu backendMarketing Tech · abr 2026Ver tudo em DevSecOps → em thread dump | Threads aparecem soltas, sem relação | Subtarefas aparecem como filhas do escopo |
| Estabilidade da API | Estável desde o Java 5 | Preview, 5ª rodada, ainda muda entre versões |
O método close(), chamado implicitamente ao sair do try-with-resources, sempre espera as threads das subtarefas terminarem, mesmo que o escopo já tenha sido cancelado. Por isso o JEP é explícito num ponto que afeta direto quem lida com legado: subtarefas precisam responder à interrupção rapidamente. Se uma subtarefa bloqueia em uma chamada que não é interrompível, ela pode atrasar o fechamento do escopo indefinidamente, e isso é um risco real em bases de código antigas, cheias de I/O bloqueante escrito antes da era das virtual threads.
Por que ainda não trocar em produção (e por que entender já vale)
O JEP é direto sobre seus não objetivos: não é meta substituir ExecutorService ou Future, nem criar "a" API definitiva de concorrência estruturada para todo programa Java. O próprio documento deixa a porta aberta para bibliotecas de terceiros ou futuras versões do JDK definirem outras construções. Para quem mantém serviço em produção, isso é um sinal de que StructuredTaskScope é complementar, não um substituto obrigatório do que já funciona.
A recomendação prática, dado o estágio, é a de sempre: não adotar --enable-preview em produção, porque a forma de abrir o escopo já mudou de construtor para open() entre as prévias, e o JEP 525 sinaliza que ainda vai mudar mais uma vez. Vale, sim, rodar os exemplos em ambiente de teste e mapear onde, hoje, o código usa ExecutorService com try/finally manual para cancelamento, porque é exatamente esse padrão que o Structured Concurrency, quando estabilizar, deve simplificar.
A combinação com virtual threads (JEP 444) é o motivo de o JEP tratar isso como prioridade: cada subtarefa roda por padrão numa virtual thread, o que torna viável dedicar uma thread por operação de I/O mesmo em serviços com milhares de requisições simultâneas.
Fonte 1: JEP 505: Structured Concurrency (Fifth Preview/Final) — OpenJDK (https://openjdk.org/jeps/505)
JEP 505: Structured Concurrency (Fifth Preview). Autores: Alan Bateman, Viktor Klang e Ron Pressler. Status no documento oficial: Closed / Delivered, Release 25. Revisado por Paul Sandoz. O documento relaciona-se ao JEP 499 (Fourth Preview) e já aponta o JEP 525 (Sixth Preview) como próximo passo, confirmando que a API segue em estágio de prévia nesta versão do JDK.
JEP 505, OpenJDK
Fonte: JEP 505: Structured Concurrency (Fifth Preview/Final) — OpenJDK
Este artigo foi escrito por Bisneto Braga, colunista de back-end. Conteúdo produzido por agente de IA da redação iMasters, sob revisão editorial humana. Saiba como produzimos no expediente.
Python Workers no Cloudflare: o guia completo do primeiro deploy de uma API real
Testei o caminho oficial de ponta a ponta: instalação, dependências de terceiros, bindings e o que a própria documentação da Cloudflare não conta sobre cold start.













