2023๋ 9์ 21์ผ์ java 21์ด ๋ฐฐํฌ๊ฐ ๋์๋ค. java 21์์ ์๋กญ๊ฒ ์๊ฐ๋ ํญ๋ชฉ ์ค ํ๋๊ฐ ๊ธฐ์กด์ ์ค๋ ๋๋ณด๋ค ๋ ๊ฒฝ๋ํ๋์ด ์ค๊ณ๋ virtual thread (๊ฐ์ ์ค๋ ๋)๋ผ๊ณ ํ ์ ์๊ฒ ๋ค. ์ด๋ฒ ํฌ์คํ ์์ ์๋กญ๊ฒ ์๊ฐ๋ virtual thread(์ดํ ๊ฐ์ ์ค๋ ๋)์ ๋ํด์ ์ ๋ฆฌํด ๋ณด๊ณ ์ ํ๋ค.
๊ธฐ์กด ์ค๋ ๋ ๋ชจ๋ธ์ ๋ฌธ์ ์
- ์ ํต์ ์ธ ์ค๋ ๋๋ java.lang.Thread ํด๋์ค์ ์ธ์คํด์ค์ด๋ค.
- ํ๋ซํผ ์ค๋ ๋๋ผ๊ณ ๋ถ๋ฆฐ๋ค.
ํ๋ซํผ ์ค๋ ๋
- ํ๋ซํผ ์ค๋ ๋(user thread)๋ OS ์ค๋ ๋(kernel thread)์ 1:1 ๋งตํ๋๋ค.
- ์ฌ์ฉ๊ฐ๋ฅํ ํ๋ซํผ ์ค๋ ๋์ ์๋ OS ์ค๋ ๋ ์๋ก ์ ํ๋๋ค.
- ์ด์ ์ฒด์ ์์ ์ ์ง ๊ด๋ฆฌํ๋ ๋น๊ต์ ํฐ ์ค๋ ๋ ์คํ ๋ฐ ๋ฆฌ์์ค๊ฐ ์๋ชจ๋์ด ์ ํ์ ์ผ ์ ์๋ค.
- ํ๋ซํผ ์ค๋ ๋ ์์ฑ์ ๋ฐ๋ฅธ ์ค๋ฒํค๋๋ฅผ ํผํ๊ธฐ ์ํด ๋ณดํต ์ค๋ ๋ ํ์ ์ฌ์ฉํ๋ค.
ํ๋ซํผ ์ค๋ ๋ ํ์ฅ์ฑ ๋ฌธ์
- ํ๋ซํผ ์ค๋ ๋๋ ๋น์ฉ์ด ๋น์ธ๊ธฐ ๋๋ฌธ์ ๋ฌดํ์ ์์ฑํ์ฌ ์ฌ์ฉํ ์๊ฐ ์๋ค.
- ๋ณดํต์ ์์ฒญ๋น ํ๋์ ํ๋ซํผ ์ค๋ ๋์ ํ ๋นํ์ฌ ์ฒ๋ฆฌํ๋๋ฐ ์ด๋ฅผ thread-per-request ํจํด์ด๋ผ๊ณ ํ๋ค.
- ์ด๋ฌํ ํจํด์ ์๋ฒ๊ฐ ์ฒ๋ฆฌํ ์ ์๋ ๋์ ์์ฒญ์ ์๊ฐ ์๋ฒ์ ํ๋์จ์ด ์ฑ๋ฅ์ ์ ๋น๋กํ๋ฏ๋ก ์๋ฒ์ ์ฒ๋ฆฌ๋์ ์ ์ฝ์ด ๋ฐ๋ฅธ๋ค.
- ํ๋ซํผ ์ค๋ ๋ ๋ด์์ ๋ค๋ฅธ ์๋น์ค์ ๋ฐ์ดํฐ ์์ฒญ์ ์ํ ๋ธ๋กํน I/O๊ฐ ๋ฐ์์ ํ๋ซํผ ์ค๋ ๋๋ ์ ํด ์ํ๋ก ์ ์ง๋๋ค.
- ์ด๋ ์ปดํจํ ๋ฆฌ์์ค์ ๋ญ๋น์ด๊ณ ๋์ ์ฒ๋ฆฌ๋์ ์ง์ํ๊ธฐ ์ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌํํ๋๋ฐ ์์ด์ ์ฃผ์ ์ฅ์ ๋ฌผ์ด ๋๋ค.
Reactive Programming ์ด์
- Reactive Programming์ ๋ค๋ฅธ ์์คํ ์ ์๋ต์ ๊ธฐ๋ค๋ฆฌ๋ ํ๋ซํผ ์ค๋ ๋์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๋์์ด๋ค.
- ๋น๋๊ธฐ API๋ ์๋ต์ ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ์ฝ๋ฐฑ์ ํตํด์ ์๋ํ๋ค.
- ์ค๋ ๋๊ฐ ๋น๋๊ธฐ API๋ฅผ ํธ์ถํ ๋๋ง๋ค ์๊ฒฉ ์์คํ ์ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋ต์ด ์ฌ ๋๊น์ง ํ๋ซํผ ์ค๋ ๋๊ฐ ํ๋ก ๋ฐํ๋๋ค.
- ๋์ค์ ์๋ต์ด ์ค๋ฉด JVM์ ํ์์ ์๋ต์ ์ฒ๋ฆฌํ ๋ค๋ฅธ ์ค๋ ๋๋ฅผ ํ ๋นํ๋ค.
- ์ด๋ฌํ ์ด์ ๋ก ํ๋์ ๋น๋๊ธฐ ์์ฒญ์ ์ฒ๋ฆฌํ๋๋ฐ ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๊ด์ฌํ๊ฒ ๋๋ค.
- ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ ์ง์ฐ์๊ฐ์ ์ ๊ฑฐ ๋์ง๋ง ์ฌ์ ํ ํ๋ซํผ ์ค๋ ๋ ์์ ์ ์ฝ์ด ์์ผ๋ฏ๋ก ํ์ฅ์ฑ์ ํ๊ณ๊ฐ ์๋ค.
virtual thread (๊ฐ์ ์ค๋ ๋)
- ํ๋ซํผ ์ค๋ ๋์ ๋ง์ฐฌ๊ฐ์ง๋ก java.lang.Thread์ ์ธ์คํด์ค๋ค.
- ํ๋ซํผ ์ค๋ ๋์ ๋ฌ๋ฆฌ OS ์ค๋ ๋์ ์ง์ ์ฐ๊ฒฐ๋์ง ์๋๋ค.
- ๊ฐ์ ์ค๋ ๋์์ ์คํ์ค์ธ ์ฝ๋๊ฐ ๋ธ๋กํน ๋๋ฉด ์คํ ๊ฐ๋ฅํ ๋ค๋ฅธ ๊ฐ์ ์ค๋ ๋๊ฐ ์คํ๋๋ค.
- ๊ฐ์ ์ค๋ ๋๋ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ์ ๋น์ทํ ๋ฐฉ์์ผ๋ก ๊ตฌํ๋์๋ค.
- ๋ง์ ์ค๋ ๋๋ฅผ ์๋ฎฌ๋ ์ด์ ํ๊ธฐ ์ํด Java ๋ฐํ์์ ๋ง์ ์์ ๊ฐ์ ์ค๋ ๋๋ฅผ ์ ์ ์์ OS ์ค๋ ๋์ ๋งตํํ๋ค.
- ๊ฐ์ ์ค๋ ๋๋ ๋๋ถ๋ถ์ ์๊ฐ์ ๋ธ๋ก ๋ ์ํ๋ก I/O ์์ ์ด ์๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ์์ ์ ์คํํ๋๋ฐ ์ ํฉํ๋ค.
- ๊ฐ์ ์ค๋ ๋๋ ํ๋ซํผ ์ค๋ ๋์ ๋นํด์ ๋ ๋น ๋ฅธ ์ค๋ ๋๊ฐ ์๋๋ค.
- ๊ฐ์ ์ค๋ ๋๋ ์๋(์ง์ฐ์๊ฐ ๋จ์ถ)๊ฐ ์๋ ํ์ฅ์ฑ(๋์ ์ฒ๋ฆฌ๋)์ ์ ๊ณตํ๊ธฐ ์ํ ๊ฒ์ด๋ค.
๊ฐ์ ์ค๋ ๋ ์ค์ผ์ค๋ง
- ์๋ฐ ๋ฐํ์์ ๊ฐ์ ์ค๋ ๋๊ฐ ์คํ๋ ๋ ์ค์ผ์ค๋ง์ ํ๋ค.
- ๊ฐ์ ์ค๋ ๋๋ฅผ ์ค์ผ์ค๋ง ํ ๋ ํ๋ซํผ ์ค๋ ๋์ ๊ฐ์ ์ค๋ ๋๋ฅผ ๋ง์ดํธ ํ๋๋ฐ ์ด๋ OS๋ ํ๋ซํผ ์ค๋ ๋๋ฅผ ์ค์ผ์ค๋งํ๋ค.
- ๊ฐ์ ์ค๋ ๋๊ฐ ๋ง์ดํธ ๋๋ ํ๋ซํผ ์ค๋ ๋๋ฅผ ์บ๋ฆฌ์ด๋ผ๊ณ ํ๋ค.
- ๊ฐ์ ์ค๋ ๋๊ฐ ๋ธ๋กํน ๋๋ I/O ์์ ์ ์ํํ ๋ ์บ๋ฆฌ์ด์์ ๋ง์ดํธ๊ฐ ํด์ ๋๋ค.
- ๊ฐ์ ์ค๋ ๋๊ฐ ์บ๋ฆฌ์ด์์ ๋ง์ดํธ ํด์ ๋๋ฉด ์๋ฐ ๋ฐํ์ ์ค์ผ์ค๋ฌ๊ฐ ๋ค๋ฅธ ๊ฐ์ ์ค๋ ๋๋ฅผ ์บ๋ฆฌ์ด์ ๋ง์ดํธ ํ๋ค.
- I/O ์์ ์ด ์๋ฃ๋๋ฉด ๊ฐ์ ์ค๋ ๋๋ ๋ค์ ์บ๋ฆฌ์ด์ ๋ง์ดํธ ๋๋ค.
๊ฐ์ ์ค๋ ๋์ ํน์ง
- ๊ฐ์ ์ค๋ ๋๋ stop(), suspend(), resume() ๋ฉ์๋๋ฅผ ์ง์ํ์ง ์๋๋ค.
- ์ง์ํ์ง ์๋ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ์ UnsupportedOperationException์ด ๋ฐ์ํ๋ค.
- single thread group์ ์ํ๋ค.
- NORM_PRIORITY ์ฐ์ ์์๋ก ๊ณ ์ ๋๋ค. setPriority() ํธ์ถ ํจ๊ณผ๋ ์๋ค.
- ํญ์ ๋ฐ๋ชฌ์ค๋ ๋๋ก ๋์ํ๋ค. setDaemon(false) ํธ์ถ ํจ๊ณผ๋ ์๋ค.
- JVM์ ์ข ๋ฃ ์ ์ ๋ชจ๋ ๊ฐ์ ์ค๋ ๋๊ฐ ์ข ๋ฃ๋๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ์ง ์๋๋ค.
- Thread.getAllStackTraces() ๋ฉ์๋๋ ๊ฐ์ ์ค๋ ๋์ ์คํ ์ถ์ ๋ฐ ๋งต์ ๋ํ ์ ๋ณด๋ฅผ ํฌํจํ์ง ์๋๋ค.
- Thread.isVirtual() ๋ฉ์๋๋ฅผ ํตํด ๊ฐ์ ์ค๋ ๋ ์ฌ๋ถ๋ฅผ ํ์ธํ ์ ์๋ค.
- ๊ฐ์ ์ค๋ ๋๊ฐ ๋ง์ดํธ ๋ ํ๋ซํผ ์ค๋ ๋๋ฅผ ์ฐพ์ ์ ์๋ ๋ฐฉ๋ฒ์ ์์ง ์๋ค.
๊ฐ์ ์ค๋ ๋ ์์ฑ ๋ฐ ์คํ
- Thread์ Thread.Builder API๋ ํ๋ซํผ ์ค๋ ๋์ ๊ฐ์ ์ค๋ ๋๋ฅผ ๋ชจ๋ ์์ฑํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ค.
- java.util.concurrent.Executors ํด๋์ค๋ ๊ฐ ์์ ์ ๋ํด ๊ฐ์ ์ค๋ ๋๋ฅผ ์์ํ๋ ExecutorService๋ฅผ ์์ฑํ๋ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค.
๋ค์์ Thread์ Thread.Builder API๋ฅผ ์ด์ฉํ ๊ฐ์ ์ค๋ ๋๋ฅผ ์์ฑํ๋ ์ฝ๋๋ค.
Thread thread = Thread.ofVirtual()
.start(() -> System.out.println("Hello"));
//์ค๋ ๋๊ฐ ์ข
๋ฃํ ๋๊น์ง ๋๊ธฐ.
thread.join();JavaThread.Builder builder = Thread.ofVirtual().name("MyThread");
Runnable task = () -> {
System.out.println("Running thread");
};
Thread t = builder.start(task);
System.out.println("Thread t name: " + t.getName());
t.join();Java- Thread.Builder ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ๋ฉด ์ค๋ ๋ ์ด๋ฆ๊ณผ ๊ฐ์ ์ผ๋ฐ์ ์ธ ์ค๋ ๋ ์์ฑ์ ์ฌ์ฉํ์ฌ ์ค๋ ๋๋ฅผ ๋ง๋ค ์ ์๋ค.
- Thread.Builder.ofPlatform()์ ํ๋ซํผ ์ค๋ ๋๋ฅผ ์์ฑํ๋ค.
- Thread.Builder.ofVirtual()์ ๊ฐ์ ์ค๋ ๋๋ฅผ ์์ฑํ๋ค.
๋ค์์ Executors.newVirtualThreadPerTaskExecutor() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ ์ค๋ ๋๋ฅผ ์์ฑํ๋ ์ฝ๋๋ค.
try (ExecutorService myExecutor = Executors.newVirtualThreadPerTaskExecutor()) {
Future<?> future = myExecutor.submit(() -> System.out.println("Running thread"));
future.get();
System.out.println("Task completed");
// ...
}Java- ExecutorService.submit(Runnable)์ด ํธ์ถ๋ ๋๋ง๋ค ์๋ก์ด ๊ฐ์ ์ค๋ ๋๊ฐ ์์ฑ๋์ด ์์ ์ ์คํํ๋ค.
๊ฐ์ ์ค๋ ๋ ๋์ ๊ฐ์ด๋
- ๊ฐ์ ์ค๋ ๋์ ํ๋ซํผ ์ค๋ ๋์ ๊ฐ์ฅ ํฐ ์ฐจ์ด์ ์ ๋์ผํ ํ๋ก์ธ์ค์์ ์๋ฐฑ๋ง ๊ฐ ์ด์์ ๊ฐ์ ์ค๋ ๋๋ฅผ ์ฝ๊ฒ ์คํํ ์ ์๋ค๋ ๊ฒ์ด๋ค.
- ์๋ฒ๊ฐ ๋ ๋ง์ ์์ฒญ์ ๋์์ ์ฒ๋ฆฌํ ์ ์์ด ์ฒ๋ฆฌ๋์ ๋์ด๊ณ ํ๋์จ์ด ๋ญ๋น๋ฅผ ์ค์์ผ๋ก์จ request-per-thread ๋ฐฉ์์ผ๋ก ์์ฑ๋ ์๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณด๋ค ํจ์จ์ ์ผ๋ก ์คํํ ์ ์๋ค๋ ๊ฒ์ด ๊ฐ์ ์ค๋ ๋์ ๊ฐ๋ ฅํ ํ์ด๋ค.
๋ธ๋กํน I/O API๋ฅผ ์ฌ์ฉํ๋ ๊ฐ๋จํ ๋๊ธฐ์ ์ฝ๋ ์์ฑ
- ํ๋ซํผ ์ค๋ ๋๋ฅผ ์ฐจ๋จํ๋ ๊ฒ์ ์๋์ ์ผ๋ก ๋น์ผ ๋ฆฌ์์ค๋ฅผ ๋ญ๋นํ๋ ๊ฒ์ด๋ค.
- ๊ฐ์ ์ค๋ ๋๋ ๊ฒฝ๋ ์ค๋ ๋ ์ด๋ฏ๋ก ์ฐจ๋จ ๋น์ฉ์ ์ ๋ ดํ๋ค.
- ๊ฐ์ ์ค๋ ๋์์๋ ๊ฐ๋จํ ๋๊ธฐ์ ์คํ์ผ ์ฝ๋๋ฅผ ์์ฑํ๊ณ ๋ธ๋กํน I/O API๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ํจ๊ณผ์ ์ด๋ค.
๋ค์๊ณผ ๊ฐ์ ๋ ผ๋ธ๋กํน ๋น๋๊ธฐ ์คํ์ผ๋ก ์์ฑ๋ ์ฝ๋๋ ๊ฐ์ ์ค๋ ๋์ ์ด์ ์ ํฌ๊ฒ ๋๋ฆฌ์ง ๋ชปํ๋ค.
CompletableFuture.supplyAsync(info::getUrl, pool)
.thenCompose(url -> getBodyAsync(url, HttpResponse.BodyHandlers.ofString()))
.thenApply(info::findImage)
.thenCompose(url -> getBodyAsync(url, HttpResponse.BodyHandlers.ofByteArray()))
.thenApply(info::setImageData)
.thenAccept(this::process)
.exceptionally(t -> { t.printStackTrace(); return null; });Java๋ฐ๋ฉด์ ๋ธ๋กํน ๋๊ธฐ์ ์คํ์ผ๋ก ์์ฑ๋ ๋ค์์ ์ฝ๋๋ ๊ฐ์ ์ค๋ ๋์์ ํฐ ํจ๊ณผ๋ฅผ ์ป์ ์ ์๋ค.
try {
String page = getBody(info.getUrl(), HttpResponse.BodyHandlers.ofString());
String imageUrl = info.findImage(page);
byte[] data = getBody(imageUrl, HttpResponse.BodyHandlers.ofByteArray());
info.setImageData(data);
process(info);
} catch (Exception ex) {
t.printStackTrace();
}Java๊ฐ์ ์ค๋ ๋๋ฅผ ํ๋ง ํ์ง ์๊ธฐ
๊ฐ์ ์ค๋ ๋๋ ์๋ฐฑ๋ง ๊ฐ ๊น์ง ์์ฑํ ์ ์์ ๋งํผ ๊ฒฝ๋ํ๋ ์ค๋ ๋๋ค.
์๋์ ์ผ๋ก ๋น์ผ ํ๋ซํผ ์ค๋ ๋์ ์์๋ ์ ์ฝ์ด ์๊ธฐ ๋๋ฌธ์ ํ๋ง์ ์ฌ์ฉํ์ฌ ๊ณผ๋ํ ๋ฆฌ์์ค ์ฌ์ฉ์ ๋ณดํธํ์์ง๋ง ๊ฐ์ ์ค๋ ๋๋ ๊ทธ๋ด ํ์๊ฐ ์๋ค.
ํ๋ซํผ ์ค๋ ๋๋ฅผ n๊ฐ์ ๊ฐ์ ์ค๋ ๋๋ก ๋ณํํ๋ ๊ฒ์ ๊ฑฐ์ ์ด์ ์ด ์์ผ๋ฉฐ, ๋ณํ์ด ํ์ํ ๊ฒ์ ๊ฐ์ ์ค๋ ๋ ๋ด์์ ์คํ๋ Task๋ค.
๋ชจ๋ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ ์ค๋ ๋๋ก ํํํ๋ ค๋ฉด ๋ค์ ์ฝ๋์ฒ๋ผ shared thread pool executor๋ฅผ ์ฌ์ฉํ์ง ์๋ ๊ฒ์ด ์ข๋ค.
Future<ResultA> f1 = sharedThreadPoolExecutor.submit(task1);
Future<ResultB> f2 = sharedThreadPoolExecutor.submit(task2);
// ... use futuresJava๋์ ์ ๋ค์ ์ฝ๋์ ๊ฐ์ด virtual thread executor๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
Future<ResultA> f1 = executor.submit(task1);
Future<ResultB> f2 = executor.submit(task2);
// ... use futures
}JavaExecutors.newVirtualThreadPerTaskExecutor()์์ ๋ฐํ๋๋ ๊ฒ์ ์ค๋ ๋ ํ์ ์ฌ์ฉํ์ง ์๋๋ค. ๋์ submit()์ ์ํด ์คํ๋๋ ๊ฐ ์์
์ ๋ํด์ ์๋ก์ด ๊ฐ์ ์ค๋ ๋๋ฅผ ์์ฑํ๋ค.
ExecutorService ์์ฒด๋ ๊ฐ๋ณ๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ๊ฐ๋จํ ๊ฐ์ฒด์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค ์ ์๋ค.
try ๋ธ๋ก์ด ๋๋ ๋ ์์์ ์ผ๋ก ํธ์ถ๋๋ close() ๋ฉ์๋๋ ExecutorService์ ์ ์ถ๋ ๋ชจ๋ ์์
, ์ฆ ExecutorService์ ์ํด ์์ฑ๋ ๋ชจ๋ ๊ฐ์ ์ค๋ ๋๊ฐ ์ข
๋ฃ๋ ๋๊น์ง ์๋์ผ๋ก ๋๊ธฐํ๋ค.
์์ ๊ฐ์ ํจํด์ ๋ค์ ์ํ ์ฝ๋์ ๊ฐ์ด ์๋ก ๋ค๋ฅธ ์๋น์ค์ ๋ํ ์ฌ๋ฌ ๋ฐ์ ํธ์ถ์ ๋์์ ์ํํ๋ ค๋ ์๋๋ฆฌ์ค์ ์ ์ฉํ๋ค.
void handle(Request request, Response response) {
var url1 = ...
var url2 = ...
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var future1 = executor.submit(() -> fetchURL(url1));
var future2 = executor.submit(() -> fetchURL(url2));
response.send(future1.get() + future2.get());
} catch (ExecutionException | InterruptedException e) {
response.fail(e);
}
}
String fetchURL(URL url) throws IOException {
try (var in = url.openStream()) {
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
}Java์๊ณ ์๋ช ์ด ์งง์ ๋์ ์์ ์ ๊ฒฝ์ฐ์๋ ์์ ๊ฐ์ด ์ ๊ฐ์ ์ค๋ ๋๋ฅผ ๋ง๋๋ ๊ฒ์ด ์ข๋ค.
์ธ๋งํฌ์ด๋ฅผ ์ฌ์ฉํ์ฌ ๋์์ฑ ์ ํ
- ์ธ๋ถ ์๋น์ค์์ ๋์ ์ ์ ์ฒ๋ฆฌ์ ํ๊ณ๋ก ์ธํ์ฌ ๊ฐ์ ์ค๋ ๋๋ฅผ ํตํ ๋์์ฑ ์์ ์ ์ ํ์ ๊ฑธ์ด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค.
- ๊ธฐ์กด ์ค๋ ๋์ ์ค๋ ๋ ํ์ ๋ค์ ์์์ฒ๋ผ ๋์์ฑ์ ์ ํํ๋ ์ญํ ์ ํ๊ธฐ๋ ํ์๋ค.
ExecutorService es = Executors.newFixedThreadPool(10);
...
Result foo() {
try {
var fut = es.submit(() -> callLimitedService());
return f.get();
} catch (...) { ... }
}Java์ ์ํ ์ฝ๋๋ ์ ํ๋ ์๋น์ค์ ๋ํ ๋์ ์์ฒญ์ 10๊ฐ๊น์ง๋ง ํ์ฉํ๋ค.
ํ์ง๋ง ๊ฐ์ ์ค๋ ๋๋ ๊ฐ์ผ ๋งํผ ํฌ์ํ์ง๋ ์๊ธฐ ๋๋ฌธ์ ํ๋ง์ ํด์๋ ์๋๋ค.
๊ฐ์ ์ค๋ ๋๋ฅผ ์ฌ์ฉํ ๋ ์ผ๋ถ ์๋น์ค์ ๋ํ ์ก์ธ์ค์ ๋์์ฑ์ ์ ํํ๋ ค๋ฉด Semaphore ํด๋์ค๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
Semaphore sem = new Semaphore(10);
...
Result foo() {
sem.acquire();
try {
return callLimitedService();
} finally {
sem.release();
}
}Java์ ์ฝ๋๋ ๊ฐ์ ์ค๋ ๋์์ ์คํ๋ ์์
์ฝ๋๋ค. Semaphore๋ฅผ ํตํด์ ํ ๋ฒ์ 10๊ฐ๊น์ง ์ ํ๋ ์๋น์ค๋ฅผ ํธ์ถํ๋๋ก ์ ํ๋ค.
์ธ๋ถ ์๋น์ค๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ธ ๊ฒฝ์ฐ connection pool ์์ฒด๊ฐ ์ธ๋งํฌ์ด ์ญํ ์ ํ๋ค. connection pool์ด 10๊ฐ์ ์ฐ๊ฒฐ๋ก ์ ํ๋๋ฉด ์ฐ๊ฒฐ์ ํ๋ํ๋ ค๋ 11๋ฒ์งธ ์ค๋ ๋๊ฐ ์ฐจ๋จ๋๋ค. connection pool ์์ ์ถ๊ฐ์ ์ธ semaphore๋ฅผ ์ถ๊ฐํ ํ์๋ ์๋ค.
ThreadLocal ๋ณ์์ ๊ฐ๋น์ผ ์ฌ์ฌ์ฉ ๊ฐ์ฒด ์บ์ํ์ง ์๊ธฐ
- ๊ฐ์ ์ค๋ ๋ ์ญ์ ThreadLocal ๋ณ์๋ฅผ ์ง์ํ์ง๋ง ๋ณด๋ค ์์ ํ๊ณ ํจ์จ์ ์ธ Scoped Value๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
- JDK 21์์๋ ํ์ฌ preview ๊ธฐ๋ฅ์ผ๋ก Scoped Value๊ฐ ์ ๊ณต๋๊ณ ์๋ค. Scoped Value์ ๋ํด์๋ ์๋ ํฌ์คํ ์์ ์ ๋ฆฌํ์๋ค.
- ThreadLocal ๋ณ์์ ์ฉ๋๋ก ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ๊ฐ์ฒด๋ฅผ ์บ์ฑํ๋ ๊ฒ์ธ๋ฐ ์ด๋ฌํ ๊ฐ์ฒด๋ ์ผ๋ฐ์ ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ง์ด ์ฌ์ฉํ๊ณ ๋ณ๊ฒฝ ๊ฐ๋ฅํ๋ฉฐ ์ค๋ ๋์ ์์ ํ์ง ์๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์ ํผํ๋ ๊ฒ์ด ์ข๋ค.
์๋ฅผ ๋ค์ด SimpleDateFormat์ ์ธ์คํด์ค๋ ์์ฑ ๋น์ฉ์ด ๋ง์ด ๋ค๊ณ thread safe ํ์ง ์๋ค.
๋ค์ ์ฝ๋๋ SimpleDateFormat ์ธ์คํด์ค๋ฅผ ThreadLocal์ ์บ์ ํ๋ ์ฝ๋๋ค.
static final ThreadLocal<SimpleDateFormat> cachedFormatter =
ThreadLocal.withInitial(SimpleDateFormat::new);
void foo() {
...
cachedFormatter.get().format(...);
...
}Java์์ ๊ฐ์ ์บ์ฑ์ ํ๋ซํผ ์ค๋ ๋๊ฐ ํ๋ง ๋ ๊ฒฝ์ฐ์ฒ๋ผ ์ค๋ ๋ ๋ก์ปฌ์ ์บ์ ๋ ๊ณ ๊ฐ์ ๊ฐ์ฒด๊ฐ ์ฌ๋ฌ ์์
์์ ๊ณต์ ๋๊ณ ์ฌ์ฌ์ฉ๋๋ ๊ฒฝ์ฐ์๋ง ์ ์ฉํ๋ค. ์ค๋ ๋ ํ์์ ์คํํ ๋ ๋ง์ ์์
์ด foo()๋ฅผ ํธ์ถํ ์ ์์ง๋ง, ํ ์ค๋ ๋๋น ํ
๋ฒ์ฉ๋ง ์ธ์คํด์คํ๋๊ณ ์ฌ์ฌ์ฉ๋๋ ๊ฒ์ ํ์ ๊ฐ์๊ฐ ์ ํ์ ์ด๊ธฐ ๋๋ฌธ์ ๋ฌธ์ ๋์ง ์๋๋ค.
๋ฐ๋ฉด์ ๊ฐ์ ์ค๋ ๋๋ ํ๋ง ๋์ง ์๊ณ ๊ด๋ จ ์๋ ์์ ์์ ์ฌ์ฌ์ฉ๋์ง ์๋๋ค. ๊ฐ์ ์ค๋ ๋์์ ์คํ๋๋ ๋ชจ๋ ์์ ์์ foo()๋ฅผ ํธ์ถํ ๋๋ง๋ค ์๋ก์ด SimpleDateFormat์ ์ธ์คํด์คํ๊ฐ ํธ๋ฆฌ๊ฑฐ ๋๋ค. ๋ํ ๋์์ ์คํ๋๋ ๊ฐ์ ์ค๋ ๋๊ฐ ๋งค์ฐ ๋ง์ ์ ์๊ธฐ ๋๋ฌธ์ ๊ฐ๋น์ผ ๊ฐ์ฒด์ ๊ฒฝ์ฐ์๋ ์๋นํ ๋ง์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์๋นํ ์ ์๋ค๋ ๊ฒ์ ์ฃผ์ํด์ผ ํ๋ค.
Synchronized ๋ธ๋ก ๋์ ReentrantLock ์ฌ์ฉ
ํ์ฌ ๊ฐ์ ์ค๋ ๋ ๊ตฌํ์ ํ๊ณ๋ ๋๊ธฐํ๋ ๋ธ๋ก ๋ด๋ถ์์ ๋ธ๋กํน ๋๋ ์์
์ ์ํํ๋ฉด ๊ฐ์ ์ค๋ ๋๋ฅผ ๋ง์ดํธ ํ๊ณ ์๋ ํ๋ซํผ ์ค๋ ๋(์บ๋ฆฌ์ด)๋ ํจ๊ป ๋ธ๋กํน๋๋ ๊ฒ์ด๋ค. (OS ์ค๋ ๋ pinning์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค.)
๋ธ๋ก ๋๋ ์๊ฐ์ด ๊ธธ๊ณ ๋น๋ฒํ ๊ฒฝ์ฐ pinning์ ์๋ฒ์ ์ฒ๋ฆฌ๋์ ๋ถ์ ์ ์ธ ์ํฅ์ ๋ฏธ์น ์ ์๋ค.
๋ค๋ง, ์ธ๋ฉ๋ชจ๋ฆฌ ์์ ๊ณผ ๊ฐ์ด ์๋ช ์ด ์งง์ ์์ ์ด๋ ๋น๋ฒํ์ง ์๊ฒ ํธ์ถ๋๋ synchronized ๋ธ๋ก์ ๋ถ์ ์ ์ธ ์ํฅ์ ๋ฏธ์น์ง ์๋๋ค.
OS ์ค๋ ๋๊ฐ pinning ๋๋ ๊ฒฝ์ฐ๋ ๋ค์ ๋ ๊ฐ์ง์ด๋ค.
- synchronized ๋ธ๋ก ํน์ ๋ฉ์๋ ์์ ์ฝ๋๊ฐ ์คํ๋๋ ๊ฒฝ์ฐ
- ๋ค์ดํฐ๋ธ ๋ฉ์๋ ํน์ ์ธ๋ถ ํจ์๋ฅผ ์คํํ๋ ๊ฒฝ์ฐ
synchronized ๋ธ๋ก์ ํ๋ซํผ ์ค๋ ๋์ ์ ์ฌํ๊ฒ ์ ํ๋ฆฌ์ผ์ด์
์ ์ฒ๋ฆฌ๋์ ์ ํํ ์ ์๋ค.
์ด๋ฅผ ๊ฐ์ ํ๊ธฐ ์ํด์ synchronized ๋ธ๋ก ๋์ ์ ReentrantLock์ ์ฌ์ฉํด์ผ ํ๋ค.
๋ค์ ์ฝ๋์ frequentIO() ๋ฉ์๋๋ ์๋ช ์ด ๊ธด IO ๋ธ๋กํน ์์ ์ ์ฒ๋ฆฌํ๋ ๋ฉ์๋๋ผ๊ณ ๊ฐ์ ํ์ ๋
synchronized(lockObj) {
frequentIO();
}Java๋ค์๊ณผ ๊ฐ์ด ์์ ํด์ผ ํ๋ค.
final ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
frequentIO();
} finally {
lock.unlock();
}JavafrequentIO๋ ์๋ช
์ด ๊ธด ๋ธ๋กํน IO๋ฅผ ์ํํ๋ ๋ฉ์๋์ธ ๊ฒฝ์ฐ์ ReentrantLock์ผ๋ก ๋์ฒดํ๋ ๊ฒ์ด ์ข๋ค.
์์์ ์ธ๊ธํ๋ฏ์ด ์๋ช
์ด ์งง์ ์์
์ด๋ ๋น๋ฒํ์ง ์๊ฒ ํธ์ถ๋๋(์๋ฅผ ๋ค์ด ์์ ์์๋ง ์ํ๋๋) synchronized ๋ธ๋ก์ ReentrantLock์ผ๋ก ๋์ฒดํ์ง ์์๋ ๋๋ค.
๊ฐ์ ์ค๋ ๋ ๋๋ฒ๊น
- ๋๋ฒ๊ฑฐ๋ ํ๋ซํผ ์ค๋ ๋์ ๊ฐ์ด ๊ฐ์ ์ค๋ ๋๋ฅผ ์ดํด๋ณผ ์ ์๋ค.
- JDK Flight Recorder(JFR)๋ฅผ ํตํด์ ๊ฐ์ ์ค๋ ๋๋ฅผ ์ดํด ๋ณผ ์ ์๋ค.
- jdk.VirtualThreadStart ๋ฐ jdk.VirtualThreadEnd๋ ๊ฐ์ ์ค๋ ๋๊ฐ ์์ ๋ฐ ์ข ๋ฃ๋๋ ์์ ์ ๋ํ๋ธ๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ๋นํ์ฑํ๋์ด ์๋ค. JDK Mission Control(JMC)์ ํตํด์ ์ฌ์ฉ์ ์ง์ JFR ๊ตฌ์ฑ์ ์ฌ์ฉํ์ฌ jdk.VirtualThreadStart ๋ฐ jdk.VirtualThreadEnd ์ด๋ฒคํธ๋ฅผ ํ์ฑํํ ์ ์๋ค.
- jdk.VirtualThreadPinned๋ ๊ฐ์ ์ค๋ ๋๊ฐ ์๊ณ ๊ธฐ๊ฐ๋ณด๋ค ์ค๋ pinned ๋์์์ ๋ํ๋ธ๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก 20ms ์๊ณ๊ฐ์ผ๋ก ํ์ฑํ๋๋ค.
- jdk.VirtualThreadSubmitFailed๋ ๋ฆฌ์์ค ๋ฌธ์ ๋ก ์ธํด ๊ฐ์ ์ค๋ ๋ ํํน ํน์ ์์ํ์ง ๋ชปํ์์ ๋ํ๋ธ๋ค. ๊ฐ์ ์ค๋ ๋๋ฅผ ํํนํ๋ฉด ๊ธฐ๋ณธ ์บ๋ฆฌ์ด ์ค๋ ๋๊ฐ ๋ค๋ฅธ ์์ ์ ์ํํ ์ ์๋๋ก ํด์ ๋๊ณ , ๊ฐ์ ์ค๋ ๋๋ฅผ ํํน ํด์ ํ๋ฉด ๊ณ์ ์งํ๋๋๋ก ์์ฝ๋๋ค. ์ด ์ด๋ฒคํธ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋์ด ์๋ค.
- ๋ นํ๋ ํ์ผ์ ์ด๋ฆ์ด recording.jfr์ด๋ผ๊ณ ํ์ ๋ ๋ค์ ๋ช ๋ น์ ํตํด์ ๊ฐ์ ์ค๋ ๋ ์ด๋ฒคํธ๋ฅผ ํ์ธํ ์ ์๋ค,
jfr print --events \
jdk.VirtualThreadStart,jdk.VirtualThreadEnd,jdk.VirtualThreadPinned, \
jdk.VirtualThreadSubmitFailed recording.jfrPlaintext- jcmd tool์ ํตํด์ ๊ฐ์ ์ค๋ ๋๋ฅผ ์ดํด๋ณผ ์ ์๋ค.
- plain text ํ์๋ฟ ์๋๋ผ JSON ํ์์ผ๋ก๋ ์ค๋ ๋ ๋คํ๋ฅผ ์์ฑํ ์ ์๋ค.
- jcmd ์ค๋ ๋ ๋คํ์๋ I/O ์์ ์์ ๋ธ๋ก ๋ ๊ฐ์ ์ค๋ ๋ ๋ฐ ExecutorService์ ์ํด์ ์์ฑ๋ ๊ฐ์ ์ค๋ ๋ ์ ๋ณด๋ฅผ ํ์ธํ ์ ์๋ค.
jcmd <PID> Thread.dump_to_file -format=text <file>
jcmd <PID> Thread.dump_to_file -format=json <file>Plaintext์ง๊ธ๊น์ง ์ ๋ฆฌํ ๋ด์ฉ์ ์ค๋ผํด ๊ณต์ ๋ฌธ์์ ๋ด์ฉ์ ๋๋ถ๋ถ ์ ๋ฆฌํ ๊ฒ์ด๋ค.
๊ฐ์ ์ค๋ ๋์ ๋ํ ๋ด์ฉ์ ๊ฒ์ํ๋ค๊ฐ ์ฝ๋ ๋ ๋ฒจ์์ ๊ฐ์ ์ค๋ ๋์ ๋์ ๋ฐฉ์์ ๋ํด์ ์ ์ ๋ฆฌํ ๋ธ๋ก๊ทธ๋ ์๊ฐํ๋ค.
https://perfectacle.github.io/2022/12/29/look-over-java-virtual-threads/#more
์ฐธ๊ณ ๋งํฌ
์ค๋ผํด ๊ณต์ ๋ฌธ์
JEPS 444
https://howtodoinjava.com/java/multi-threading/virtual-threads
https://dev.java/learn/new-features/virtual-threads

