
Java structured concurrency는 동시성 코드를 더 빠르게 만드는 기능이라기보다, 여러 작업의 수명을 더 안전하게 다루려는 방식입니다.
서버 요청 하나를 처리하면서 사용자 정보, 주문 정보, 추천 정보를 동시에 가져온다고 생각해보겠습니다. 이 작업들은 따로 실행되지만 논리적으로는 하나의 요청에 속합니다.
structured concurrency는 이런 하위 작업들을 부모 작업의 생명주기 안에 묶어서 실패, 취소, 관찰을 더 명확하게 만들려는 접근입니다.
Java structured concurrency를 한 줄로 정리하기

JEP 525는 structured concurrency를 서로 다른 thread에서 실행되는 관련 작업들을 하나의 작업 단위로 다루는 API라고 설명합니다.
핵심은 병렬 실행 자체가 아니라, 하위 작업이 부모 작업보다 오래 살아남지 않게 하고 실패와 취소를 구조적으로 전파하는 것입니다.
ExecutorService만으로는 관계가 코드에 잘 드러나지 않는다
ExecutorService와 Future는 오랫동안 Java 동시성의 기본 도구였습니다. 문제는 작업 간 관계가 코드 구조에 충분히 드러나지 않는다는 점입니다.
JEP 525의 예시처럼 한 요청에서 findUser()와 fetchOrder()를 동시에 실행할 때, 한쪽이 실패해도 다른 작업이 계속 돌 수 있습니다. 호출한 작업이 실패했는데 하위 작업이 남아 있으면 thread leak이나 불필요한 작업이 생깁니다.
Response handle() throws ExecutionException, InterruptedException {
Future<String> user = executor.submit(() -> findUser());
Future<Integer> order = executor.submit(() -> fetchOrder());
return new Response(user.get(), order.get());
}이 코드는 짧지만 실패 전파와 취소 처리가 숨어 있습니다. 둘 중 하나가 실패했을 때 나머지를 어떻게 취소할지, 부모 작업이 중단되면 하위 작업도 멈추는지 별도로 챙겨야 합니다.
structured concurrency는 block 구조로 수명을 묶는다
structured concurrency의 아이디어는 단순합니다. 하위 작업은 부모 작업의 block 안에서 시작되고, 그 block을 벗어나기 전에 완료되거나 취소되어야 합니다.
이렇게 하면 코드를 읽는 사람이 작업 관계를 눈으로 볼 수 있습니다. 관찰 도구에서도 어떤 작업이 어떤 요청의 하위 작업인지 더 잘 드러날 수 있습니다.
try (var scope = StructuredTaskScope.open(
StructuredTaskScope.Joiner.allSuccessfulOrThrow())) {
var user = scope.fork(() -> findUser());
var order = scope.fork(() -> fetchOrder());
scope.join();
return new Response(user.get(), order.get());
}정확한 API 형태는 preview 단계에서 계속 조정되고 있습니다. 따라서 실무 글이나 예제 코드를 볼 때는 사용 중인 JDK 버전의 JEP와 문서를 반드시 확인해야 합니다.
virtual threads와 함께 봐야 하는 이유
virtual threads는 I/O 중심 서버 코드에서 thread를 더 많이, 더 싸게 쓸 수 있게 만들었습니다. 하지만 thread를 많이 만들 수 있다는 것과 그 thread들의 수명을 잘 관리한다는 것은 다른 문제입니다.
작업을 쉽게 많이 만들수록 실패한 작업, 취소되지 않은 작업, 어디에 속한지 모르는 작업도 늘어날 수 있습니다. structured concurrency는 이 관리 문제를 줄이는 쪽에 초점이 있습니다.
그래서 virtual threads 글을 읽었다면 다음 질문은 자연스럽게 structured concurrency로 이어집니다. “가벼운 thread를 많이 만들 수 있다면, 그 thread들을 어떤 구조로 묶어 관리할 것인가?”입니다.
JDK 26 기준 아직 preview API다
JEP 525 기준 structured concurrency는 JDK 26의 sixth preview API입니다. preview API는 실험적 성격이 있고, 사용하려면 preview 기능을 활성화해야 합니다.
javac --release 26 --enable-preview Main.java
java --enable-preview Main이 점은 실무 도입 판단에서 매우 중요합니다. 개념은 지금 익혀둘 가치가 크지만, 장기 운영 서비스에 바로 핵심 API로 넣을지는 팀의 JDK 버전, preview API 사용 정책, 프레임워크 지원을 함께 봐야 합니다.
언제 이 개념이 특히 유용할까
- 하나의 요청을 처리하기 위해 여러 I/O 작업을 동시에 실행한다
- 한 하위 작업이 실패하면 나머지 작업도 의미가 없어진다
- timeout, cancellation, failure propagation을 매번 수동으로 처리하고 있다
- thread dump를 봐도 어떤 작업이 어떤 요청에 속하는지 파악하기 어렵다
- virtual threads 도입 후 작업 수명 관리 기준이 필요하다
정리
Java structured concurrency는 동시성을 더 많이 쓰기 위한 기능이 아니라, 이미 쓰고 있는 동시성을 더 안전하게 구조화하기 위한 방향입니다.
ExecutorService와 Future가 틀렸다는 뜻은 아닙니다. JEP 525도 기존 java.util.concurrent 구성요소를 대체하는 것이 목표가 아니라고 말합니다. 다만 여러 하위 작업을 하나의 논리 작업으로 다루는 코드에서는 structured concurrency가 더 읽기 쉽고 안전한 모델을 제공합니다.
관련해서는 Java virtual threads란 무엇인가, Java virtual threads pinning이란 무엇인가도 함께 보면 좋습니다.