Java ์ ํ๋ฆฌ์ผ์ด์
์ ๊ฐ๋ฐํ๋ค ๋ณด๋ฉด ์ค๋ ๋ ์ฌ์ฉ์ ๋งค์ฐ ์ค์ํ ์์๋ค. ๊ทธ๋์ ExecutorService๋ CompletableFuture๋ฅผ ์ฌ์ฉํ๋ฉด์ ๊ธฐ๋ฅ์ ์ผ๋ก ํ๋์ ํธ๋์ญ์
๊ธฐ๋ฅ์ผ๋ก ๋ฌถ์ธ ์ฌ๋ฌ ์ค๋ ๋ ์ค์์ ํ๋๋ผ๋ ์์ธ๊ฐ ๋ฐ์ํ๋ ์ค๋ ๋๊ฐ ์๋ค๋ฉด ๋๋จธ์ง ์ค๋ ๋๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํ ๋ณต์กํ ์๋ฌ ๋ก์ง์ ์ฌ์ฉํ์ ๊ฒ์ด๋ค.(cancel() ํธ์ถ..) ํ์ง๋ง ์ด๋ฒ์ ์๊ฐํ Structured Concurrency๋ ์ด๋ฌํ ๊ณจ์น์ํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด ์ค ๊ฒ์ด๋ค. ๋ํ JDK21 ๋ฒ์ ์์ ์ ์ ๋ฆด๋ฆฌ์ฆ๋ ๊ฐ์ ์ค๋ ๋์ ๋ง์ถฐ ๋ณ๋ ฌ๋ก ์คํ ๋๋ ์ฌ๋ฌ ์ค๋ ๋์ ๋ํ ๊ด๋ฆฌ ์ธก๋ฉด์์๋ ๋ง์ ๋์์ด ๋ ๊ฒ ๊ฐ๋ค.
์ด๋ฒ ํฌ์คํ
์์๋ ๊ธฐ์กด ๋ฐฉ์์ด ์ด๋ค ๋ถ๋ถ์์ ์ํํ๋์ง ๊ทธ๋ฆฌ๊ณ Structured Concurrency๊ฐ ์ด๋ป๊ฒ ์ค๋ ๋ ๋์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด ์ฃผ๋์ง ๋ฏธ๋ฆฌ ์์๋ณด์.
Structured Concurrency ํ์คํ ๋ฆฌ
์ฐ์ ๊ฐ๋จํ Structured Concurrency์ ํ์คํ ๋ฆฌ๋ฅผ ์์ ๋ณด์.
- JEP428 JEP437 – JDK19 ์ฒ์์ผ๋ก ์ ์ ๋์๊ณ JDK 20๊น์ง Incubator ๋จ๊ณ๋ก ์์๋์๋ค.
- JEP453 – JDK21 Structured Concurrency๊ฐ Preview ์ํ๋ก ์น๊ฒฉ๋ฌ๋ค.
- JEP462 – JDK22 ์ฌ์ ํ Prewiew ์ํ๋ก API ์์ ํ ๋ฐ ํผ๋๋ฐฑ์ ๋ฐ์ํ๋ค.
- JEP480 – JDK23 Structured Concurrency์ ์ธ๋ฒ์งธ Preview ์ํ.
- JEP499 – JDK24 API ๊ฐ์ , ๊ตฌ์กฐ ๊ฐ์ ์ด ์ด๋ฃจ์ด์ก๋ค. ๋ค๋ฒ์งธ Preview ์ํ.
- JEP505 – JDK25 API ์ค๊ณ๊ฐ ๋ค๋ฌ์ด์ง๊ณ ์ ์ ๋ฐฐํฌ ์ ํ์ ์ํ ์์ ํ ๋จ๊ณ๋ก ํ๊ฐ ๋๋ค. ๋ค์ฏ ๋ฒ์งธ Preview.
- JEP525 – JDK26 ์ฐจ๊ธฐ ๋ฐฐํฌ ๋ฒ์ ์์ ์ธ 26 ๋ฒ์ ์๋ ์ฌ์ฏ ๋ฒ์งธ Preview๋ก ์ฐ๊ตฌ ์ค์ด๋ค.
์์ง Structured Concurrency๊ฐ ์์ง์ Preview ๋จ๊ณ๋ก ์คํ์ ์ด์ง๋ง ์ถํ์ ์ ์ ๊ธฐ๋ฅ์ผ๋ก ๋ฆด๋ฆฌ์ฆ ๋๋ฉด ์ค๋ ๋ ๋์ ๋ฐฉ์ง๋ฅผ ์ํด์ ์ฌ์ฉํจ๊ณผ๊ฐ ๊ฝค ํด ๊ฒ์ผ๋ก ๊ธฐ๋๋๋ค. JDK25 ๋ฒ์ ์์ API ์ค๊ณ๊ฐ ๋ค๋ฌ์ด ์ง๋งํผ ์ด์ JDK21 ๋ฒ์ ๊ณผ ๋น๊ตํด ์ฌ์ฉ๋ฒ์ด ์ผ๋ถ ๋ณ๊ฒฝ๋์๋ค.
Structured Concurrency ๋ณ๊ฒฝ ํฌ์ธํธ
์ด์ JDK ๋ฒ์ ์ ์ฌ์ฉ๋ฒ๊ณผ JDK25์์์ ์ฌ์ฉ๋ฒ์ ๋ณ๊ฒฝ๋ ๋ถ๋ถ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ํด๋์ค ์์ ์ ๊ฑฐ: ShutdownOnFailure, ShutdownOnSuccess ๊ฐ์ ์์ ๊ธฐ๋ฐ ํด๋์ค๊ฐ ์ ๊ฑฐ๋์๋ค.
- Factory Method ๋์ : new StructuredTaskScope(…) ๋์ StructuredTaskScope.open(…)์ ์ฌ์ฉํ๋ค.
- Joiner ๋์ : ‘์คํจํ๋ฉด ์ค๋จํ ๊ฒ์ธ๊ฐ’, ‘์ฑ๊ณตํ ๊ฒ๋ง ๊ฐ์ ธ์ฌ ๊ฒ์ธ๊ฐ’์ ๊ฐ์ ์ ์ฑ ์ Joiner๋ผ๋ ๊ฐ์ฒด์ ์์ํ๋ค.
- join()์ ์งํ: join() ๋ฉ์๋๊ฐ ๋จ์ํ ๋๊ธฐ๋ง ํ๋ ๊ฒ์ด ์๋ Joiner๊ฐ ์ฒ๋ฆฌํ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๊ฑฐ๋ ์์ธ๋ฅผ ๋์ง๋ค. (throwIfFailed()๋ฅผ ํธ์ถํ ํ์๊ฐ ์์ด์ก๋ค.)
๊ธฐ์กด ๋ฐฉ์์ ๋ฌธ์ ์
๋ถ๋ชจ ์์ ๊ฐ์ ๊ด๊ณ ๋จ์
๊ธฐ์กด์ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ(Unstructured Concurrency)์ ๊ฐ์ฅ ํฐ ๋ฌธ์ ๋ ์์
๊ฐ์ ๊ด๊ณ๊ฐ ๋๊ฒจ ์๋ค๋ ์ ์ด๋ค.
๋ถ๋ชจ ์ค๋ ๋์์ ์์ ์ค๋ ๋๋ฅผ ์์ฑํ๋ฉด ๋
ผ๋ฆฌ์ ์ผ๋ก ๋ถ๋ชจ-์์ ๊ด๊ณ์ด์ง๋ง JVM ์
์ฅ์์๋ ์ด ๋์ ์๋ฌด ์๊ด์๋ ์ค๋ ๋์ผ ๋ฟ์ธ ๊ฒ์ด๋ค.
public static void main() {
legacy1();
System.out.println("Main ์ค๋ ๋ ์ข
๋ฃ!!");
}
static void legacy() {
// ๊ธฐ์กด ๋ฐฉ์ (ExecutorService)
// ๋ถ๋ชจ๊ฐ ์ฃฝ์ด๋ ์์์ ์ข๋น์ฒ๋ผ ๊ณ์ ๋๋ค.
ExecutorService es = Executors.newFixedThreadPool(1);
// ์์ ์ค๋ ๋: 5์ด ๊ฑธ๋ฆฌ๋ ๋ฌด๊ฑฐ์ด ์์
Future<?> child = es.submit(() -> {
try {
Thread.sleep(5000);
System.out.println("์์: ์์
์๋ฃ! (ํ์ง๋ง ์๋ฌด๋ ๋ฃ์ง ์๋๋ค..)");
} catch (InterruptedException e) {
// ์ทจ์ ๋ก์ง์ด ์์ด๋, ํธ์ถ์ ์ ํด์ฃผ๋ฉด ๋ฌด์ฉ์ง๋ฌผ
}
});
try {
// ๋ถ๋ชจ ์ค๋ ๋: ๊ฐ์์ค๋ฌ์ด ์๋ฌ ๋ฐ์
throw new RuntimeException("๋ถ๋ชจ ์ข
๋ฃ!");
}
catch ( Exception e ) {
System.out.println("์์ธ ๋ฐ์ ๊ฐ์ง! ์๋ต์ ์คํจ๋ก ์ฒ๋ฆฌํ๊ณ ์ข
๋ฃ!");
}
es.shutdown();
}
Java์ ์ฝ๋๋ฅผ ์คํํ๋ฉด ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ๋ค.
์์ธ ๋ฐ์ ๊ฐ์ง! ์๋ต์ ์คํจ๋ก ์ฒ๋ฆฌํ๊ณ ์ข
๋ฃ!
Main ์ค๋ ๋ ์ข
๋ฃ!!
.... 5์ดํ ....
์์: ์์
์๋ฃ! (ํ์ง๋ง ์๋ฌด๋ ๋ฃ์ง ์๋๋ค..)Plaintext๋ถ๋ชจ ์ค๋ ๋์ ์์ธ๊ฐ ๋ฐ์ํ์ฌ ์ข
๋ฃ๊ฐ ๋์ด๋ ์์์ค๋ ๋๋ 5์ด๊ฐ CPU๋ฅผ ์ ์ ํ๋ฉฐ ๊ณ์ํด์ ์คํ๋๊ณ ์๋ ๊ฒ์ด๋ค.
์ด๊ฒ์ด ๋ฐ๋ก ์ค๋ ๋ ๋์(Thread Leak)์ด๋ค.
ํ์ ๊ด๊ณ๋ ๋จ์
๋ถ๋ชจ ์์ ๊ด๊ณ๊ฐ ์๋ ์ด๋ค ์๋ก ์ฐ๊ด์ฑ์ด ์๋ ๋ ์ค๋ ๋๊ฐ ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌ๋๊ณ ์๋ค๊ฐ ํด๋ณด์. ํ๋์ ์ค๋ ๋์์ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด ๋ค๋ฅธ ๋ชจ๋ ์ฒ๋ฆฌ๋ฅผ ๋ฐ๋ก ์ข ๋ฃ์ํค๊ณ ์คํจ๋ก ์ฒ๋ฆฌํ๋ ๊ฒ์ด ๋ฆฌ์์ค ์ฌ์ฉ์ ์์ด์ ๋ ์ ๋ฆฌํ ๊ฒ์ด๋ค.
public static void main() {
legacy2();
System.out.println("Main ์ค๋ ๋ ์ข
๋ฃ!!");
}
static void legacy2() {
CompletableFuture<Void> taskA = CompletableFuture.runAsync( () -> {
try {
Thread.sleep( 100 );
System.out.println("Task A: ์๋ฌ ๋ฐ์!!");
throw new RuntimeException( "Task A ์์ธ ๋ฐ์ ");
}
catch ( InterruptedException e ) {
throw new RuntimeException(e);
}
} );
CompletableFuture<Void> taskB = CompletableFuture.runAsync( () -> {
System.out.println("๋ฌด๊ฑฐ์ด ์์
์์ (5์ด ์์)");
try {
Thread.sleep( 5000 );
System.out.println("Task B: ์์
์๋ฃ! (ํ์ง๋ง ์๋ฌด๋ ๋ฃ์ง ์๋๋ค");
}
catch ( InterruptedException e ) {
throw new RuntimeException( e );
}
throw new RuntimeException( "์์ธ ๋ฐ์!!!" );
} );
CompletableFuture<Void> all = CompletableFuture.allOf( taskA, taskB );
try {
all.join();
}
catch ( Exception e ) {
System.out.println("์์ธ ๋ฐ์ ๊ฐ์ง! ์๋ต์ ์คํจ๋ก ์ฒ๋ฆฌํ๊ณ ์ข
๋ฃ!");
}
}JavaTaskA๊ณผ TaskB ๋ ๊ฐ์ ์ค๋ ๋๊ฐ ์๋ก ์ฐ๊ด๋ ํ์ ๊ด๊ณ๋ผ๊ณ ํ๋๋ผ๋ ํ์ชฝ์์ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด ๋ฐ๋ก ์ข
๋ฃ ๋์ง ์๊ณ ๋ชจ๋ ์ค๋ ๋๊ฐ ์๋ฃ๋ ๋๊น์ง ์คํํ๊ฒ ๋๋ค.
๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ๋ค.
๋ฌด๊ฑฐ์ด ์์
์์ (5์ด ์์)
Task A: ์๋ฌ ๋ฐ์!!
.... 5์ดํ ....
Task B: ์์
์๋ฃ! (ํ์ง๋ง ์๋ฌด๋ ๋ฃ์ง ์๋๋ค)
์์ธ ๋ฐ์ ๊ฐ์ง! ์๋ต์ ์คํจ๋ก ์ฒ๋ฆฌํ๊ณ ์ข
๋ฃ!
Main ์ค๋ ๋ ์ข
๋ฃ!!Plaintext์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฒ์ TaskA์์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด TaskB ์ญ์ ๋ฐ๋ก ์ข ๋ฃํ๊ณ ๋์์ ๋๋ด๋ ๊ฒ์ด์ง๋ง TaskA์์ ์์ธ๊ฐ ๋ฐ์ํด๋ TaskB๋ ๊ณ์ํด์ ์คํ๋๊ณ ์๋ค.
Structured Concurrency์ ๋์
์ด์ Structured Concurrency์ ๋์์ ์ดํด๋ณด์.
๋ถ๋ชจ ์์๊ฐ์ ๊ด๊ณ ์ ์ง
Structured Concurrency๋ ๋ถ๋ชจ ์ค๋ ๋์ ์์ ์ค๋ ๋๊ฐ์ ๊ด๊ณ๋ฅผ ์ ์งํจ์ผ๋ก์จ ๋ถ๋ชจ๊ฐ ์ข ๋ฃ๋๋ฉด ์์๋ ๋ฐ๋ก ์ข ๋ฃ๋๋ค.
public static void main() {
structuredscope1();
System.out.println("Main ์ค๋ ๋ ์ข
๋ฃ!!");
}
static void structuredscope1() {
// try-with-resources๋ก Scope ์์ฑ (์๋ close ๋ณด์ฅ)
try ( var scope = StructuredTaskScope.open(
StructuredTaskScope.Joiner.awaitAllSuccessfulOrThrow() ) ) {
// 1. Fork: ์์ ์ค๋ ๋ ์์ฑ (Scope์ ๊ด๋ฆฌ๋ฅผ ๋ฐ์)
StructuredTaskScope.Subtask<Void> fork = scope.fork( () -> {
try {
Thread.sleep( 5000 );
System.out.println("์์: ์์
์๋ฃ!");
}
catch ( InterruptedException e ) {
throw new RuntimeException( e );
}
} );
// 2. ๋ถ๋ชจ ๋ก์ง์์ ์๋ฌ ๋ฐ์ ๊ฐ์
if (true) throw new RuntimeException("๋ถ๋ชจ ์๋ฌ!");
scope.join();// ๋๊ธฐ
} catch (Exception e) {
System.out.println(e.getMessage());
// 3. try ๋ธ๋ก์ ๋ฒ์ด๋๋ ์๊ฐ scope.close()๊ฐ ํธ์ถ๋จ.
// -> ์คํ ์ค์ด๋ ์์ ์ค๋ ๋(task)์ ์ฆ์ 'Interruption' ์ ํธ๊ฐ ์ ์ก๋จ.
System.out.println("Scope๊ฐ ๋ซํ๋ฉด์ ์์ ์ค๋ ๋๊ฐ ์์ ํ๊ฒ ์ข
๋ฃ๋์์ต๋๋ค.");
}
}JavaJoiner์ awaitAllSuccessfulOrThrow()๋ ๋ชจ๋ ์์์ด ์ฑ๊ณตํ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ด๋ค. StructuredTaskScope๋ด์ ์ค๋ ๋๊ฐ 5์ด ๋์ ์ผ์ ํ๋ ๋์ ๋ถ๋ชจ ๋ก์ง์์ ์์ธ๊ฐ ๋ฐ์ํ์ ๋ ์ด๋ป๊ฒ ์ฒ๋ฆฌ๋๋์ง ๊ฒฐ๊ณผ๋ฅผ ํตํด ์์๋ณด์.
๋ถ๋ชจ ์๋ฌ!
Scope๊ฐ ๋ซํ๋ฉด์ ์์ ์ค๋ ๋๊ฐ ์์ ํ๊ฒ ์ข
๋ฃ๋์์ต๋๋ค.
Main ์ค๋ ๋ ์ข
๋ฃ!!Plaintext๋ถ๋ชจ๊ฐ ์ข ๋ฃ๋๋ฉด์ ์์ ์ค๋ ๋๊ฐ ๋ง์ง๋ง๊น์ง ์คํ๋์ง ์๊ณ ๋ฐ๋ก ์ข ๋ฃ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
ํ์ ๊ด๊ณ ์ ์ง
StructuredTaskScope๋ก ๋ฌถ์ธ ๋๊ฐ์ ์ค๋ ๋ ์์ ์ด ๋ณ๋ ฌ๋ก ์คํ๋ ๋ ํ๋์ ์ค๋ ๋์์ ์์ธ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ๋ฅผ ์ดํด๋ณด์.
public static void main() {
structuredscope2();
System.out.println("Main ์ค๋ ๋ ์ข
๋ฃ!!");
}
static void structuredscope2() {
// try-with-resources๋ก Scope ์์ฑ (์๋ close ๋ณด์ฅ)
try ( var scope = StructuredTaskScope.open(
StructuredTaskScope.Joiner.awaitAllSuccessfulOrThrow() ) ) {
// 1. Fork: ์์ ์ค๋ ๋ ์์ฑ (Scope์ ๊ด๋ฆฌ๋ฅผ ๋ฐ์)
StructuredTaskScope.Subtask<Void> taskA = scope.fork( () -> {
try {
Thread.sleep( 300 );
System.out.println("Task A: ์๋ฌ ๋ฐ์!!");
throw new RuntimeException( "Task A ์์ธ ๋ฐ์ ");
}
catch ( InterruptedException e ) {
throw new RuntimeException( e );
}
} );
StructuredTaskScope.Subtask<Void> taskB = scope.fork( () -> {
System.out.println("๋ฌด๊ฑฐ์ด ์์
์์ (5์ด ์์)");
try {
Thread.sleep( 5000 );
System.out.println("Task B: ์์
์๋ฃ!");
}
catch ( InterruptedException e ) {
throw new RuntimeException( e );
}
} );
scope.join();// ๋๊ธฐ
} catch (Exception e) {
System.out.println(e.getMessage());
// 3. try ๋ธ๋ก์ ๋ฒ์ด๋๋ ์๊ฐ scope.close()๊ฐ ํธ์ถ๋จ.
// -> ์คํ ์ค์ด๋ ์์ ์ค๋ ๋(task)์ ์ฆ์ 'Interruption' ์ ํธ๊ฐ ์ ์ก๋จ.
System.out.println("Scope๊ฐ ๋ซํ๋ฉด์ ์์ ์ค๋ ๋๊ฐ ์์ ํ๊ฒ ์ข
๋ฃ๋์์ต๋๋ค.");
}
}
Java์์ ์ค๋ช ํ๋ฏ์ด Joiner์ awaitAllSuccessfulOrThrow()๋ ๋ชจ๋ ์์ ํ์ ์ค๋ ๋๊ฐ ์ฑ๊ณตํ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ด๋ค. ํ์ ์ค ํ๋๋ผ๋ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด ๋ชจ๋ ์ค๋ ๋๋ ์ข ๋ฃํ๋ค. ๊ฒฐ๊ณผ๋ฅผ ํตํด ํ์ธํด ๋ณด์.
๋ฌด๊ฑฐ์ด ์์
์์ (5์ด ์์)
Task A: ์๋ฌ ๋ฐ์!!
java.lang.RuntimeException: Task A ์์ธ ๋ฐ์
Scope๊ฐ ๋ซํ๋ฉด์ ์์ ์ค๋ ๋๊ฐ ์์ ํ๊ฒ ์ข
๋ฃ๋์์ต๋๋ค.
Main ์ค๋ ๋ ์ข
๋ฃ!!PlaintextTaskA์์ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด TaskB์ ๋์์ด ์ข ๋ฃ๋๋ค.
StructuredTaskScope.Joiner ์ ์ฑ
StructuredTaskScope.Joiner๋ ์ฌ๋ฌ ์์ ์ค๋ ๋์ ์์ธ ๋ฐ ์๋ฃ ๊ฒฐ๊ณผ์ ๋ฐ๋ฅธ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ์ ๋ํ ๋ช๊ฐ์ง ์ ์ฑ ์ ๊ฐ์ง๊ณ ์๋ค.
- awaitAllSuccessfulOrThrow()
- ๋ชจ๋ ์์ ์ด ์ฑ๊ณตํ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ๋ค. ๋ง์ฝ ํ๋๋ผ๋ ์คํจํ๋ค๋ฉด ์ฆ์ ๋๋จธ์ง ์์ ์ ์ทจ์ํ๊ณ ์์ธ๋ฅผ ๋์ง๋ค. join() ํธ์ถ์ ๊ฒฐ๊ณผ๋ ์๋ค.
- ์๋ก ๋ค๋ฅธ ํ์ ์ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ฌ ๋ ๊ฒฐ๊ณผ๋ join()์ด ์๋ ๋ฏธ๋ฆฌ ์ ์ธํด ๋ SubTask์์ ๊ฐ์ ธ์์ผ ํ๋ค.
// ๊ฒฐ๊ณผ๊ฐ์ด ์์ผ๋ฏ๋ก Subtask ๋ณ์์์ get() ํด์ผ ํจ
try (var scope = StructuredTaskScope.open(Joiner.awaitAllSuccessfulOrThrow())) {
Subtask<String> taskA = scope.fork(() -> "A");
Subtask<Integer> taskB = scope.fork(() -> 1);
scope.join(); // ์ฌ๊ธฐ์ ์์ธ ๋ฐ์ ๊ฐ๋ฅ
// taskA.get(), taskB.get() ์ฌ์ฉ
}Java- allSuccessfulOrThrow()
- awaitAllSuccessfulOrThrow์ ๊ฐ์ด ๋์ํ์ง๋ง join() ํธ์ถ์ ์ฑ๊ณตํ ๊ฒฐ๊ณผ๋ค์ Stream<T>๋ก ๋ฌถ์ด์ ๋ฐํํ๋ค.
- ๋์ผํ ํ์ ์ ๊ฒฐ๊ณผ๋ฅผ ํ๋ฒ์ ๋ฆฌ์คํธ๋ก ๋ฐ๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ฉด ์ ์ฉํ๋ค.
// ํ์
์ <String>์ผ๋ก ๋ช
์ํด์ผ ํจ
try (var scope = StructuredTaskScope.open(Joiner.<String>allSuccessfulOrThrow())) {
scope.fork(() -> "A");
scope.fork(() -> "B");
Stream<String> results = scope.join(); // ["A", "B"] ์คํธ๋ฆผ ๋ฐํ
}Java- anySuccessfulResultOrThrow()
- ์์ ์ค ๊ฐ์ฅ ๋จผ์ ์ฑ๊ณตํ ์์ ์ด ๋์ค๋ฉด ๋๋จธ์ง ์์ ์ ์ทจ์ํ๊ณ join() ํธ์ถ์ ์ฑ๊ณตํ ์์ ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค. ๋ชจ๋ ์คํจํ๋ฉด ์์ธ๋ฅผ ๋์ง๋ค.
- ๋์ผํ ๊ธฐ๋ฅ์ ํ๋ ์ฌ๋ฌ ์๋ฒ ์ค ๊ฐ์ฅ ๋น ๋ฅธ ์๋ต์ด ํ์ํ ๊ฒฝ์ฐ ์ฌ์ฉํ๊ธฐ ์ข๋ค. (Redundancy)
try (var scope = StructuredTaskScope.open(Joiner.<String>anySuccessfulResultOrThrow())) {
scope.fork(() -> slowApi());
scope.fork(() -> fastApi());
String winner = scope.join(); // "Fast Result" ๋ฐํ
}Java- awaitAll()
- ๋๊น์ง ๊ฐ๋ค. ์ฑ๊ณต์ด๋ ์คํจ๋ ์๊ด์์ด ๋ชจ๋ ๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ๋ค. join() ํธ์ถ์ ๊ฒฐ๊ณผ๋ ์๋ค.
- ๋์๋ณด๋ ์ฒ๋ผ ์ผ๋ถ ์์ ฏ ํญ๋ชฉ์ ์๋ฌ๊ฐ ๋ฐ์ํด๋ ๋๋จธ์ง๋ ๋ณด์ฌ์ค์ผ ํ๋ ๊ฒฝ์ฐ์ ์ฌ์ฉํ๊ธฐ ์ข๊ฒ ๋ค.
try (var scope = StructuredTaskScope.open(Joiner.awaitAll())) {
var taskA = scope.fork(() -> "Success");
var taskB = scope.fork(() -> { throw new Exception("Fail"); });
scope.join(); // ์์ธ ์ ๋์ง. ๊ทธ๋ฅ ๊ธฐ๋ค๋ฆผ.
// ๊ฐ๋ฐ์๊ฐ ์ง์ ์ํ ํ์ธ
if (taskA.state() == Subtask.State.SUCCESS) { ... }
}JavaStructured Concurrency๋ ์์ง๊น์ง Preview ๊ธฐ๋ฅ์ด์ง๋ง ์กฐ๋ง๊ฐ ์ ์ ๋ฒ์ ์ผ๋ก ์ถ์๋์ง ์์๊น ๊ธฐ๋ํ๋ค.
Java 25์์ ์ ์ ๋ฆด๋ฆฌ์ฆ๋ Scoped Value์๋ ์ฐฐ๋ก๊ถํฉ์ผ๋ก ์ ๋ง๊ณ , Java 21์์ ์ ์ ๋ฆด๋ฆฌ์ฆ ๋ ๊ฐ์์ค๋ ๋๋ฅผ ๊ด๋ฆฌํ๋๋ฐ๋ ์๋นํ ์ ์ฉํ ๊ฒ์ด๋ค.
