java21 ์ฒ˜๋ฆฌ๋Ÿ‰ ํ–ฅ์ƒ์„ ์œ„ํ•œ ๋Œ€์•ˆ – virtual thread

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();
Java
Thread.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 futures
Java

๋Œ€์‹ ์— ๋‹ค์Œ ์ฝ”๋“œ์™€ ๊ฐ™์ด virtual thread executor๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
   Future<ResultA> f1 = executor.submit(task1);
   Future<ResultB> f2 = executor.submit(task2);
   // ... use futures
}
Java

Executors.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();
}
Java

frequentIO๋Š” ์ˆ˜๋ช…์ด ๊ธด ๋ธ”๋กํ‚น 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.jfr
Plaintext
  • 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