티끌모아 로키산맥 🏔
search
로ᄏl
배움에 끝은 없다... 개발 또한 그러하다.
Today
Yesterday
JDK 21(LTS)에 추가된 경량 스레드, OS 스레드를 그대로 사용하지 않고 JVM 내부 스케줄링을 통해서 수십만 ~ 수백만개의 스레드를 동시에 사용할 수 있게한다.
위와 같은 동작 매커니즘에의한 Throughput(처리량) 의 한계
특히나 Blocking I/O 쪽에서 많은 문제가 발생함.
그래서 우리는 Reactive Programming을 사용했었음.
그렇다면 왜 이렇게 구성이 되어있을까…?
결론은 Virtual Thread는 Reacitve와 MVC의 장점만 차용한 케이스!
Virtual Thread가 앞에 따로 존재함.
뒤에 Fork/Join Pool이 Carrier Thread(Platform Thread와 거의 동일한 형태)
Carrier Thread는 OS Thread와 1:1 매핑되는 구조이긴 하지만 실제 Application에서는 Platform Thread가 아니라 Virtual Thread만 사용하게된다.
// Virtual Thread 방법 1
Thread.startVirtualThread(() -> {
System.out.println("Hello Virtual Thread");
});
// Virtual Thread 방법 2
Runnable runnable = () -> System.out.println("Hi Virtual Thread");
Thread virtualThread1 = Thread.ofVirutal().start(runnable);
// Virtual Thread 이름 지정
Thread.Builder builder = Thread.ofVirtual().name("JVM-Thread");
Thread virtualThread2 = builder.start(runnable);
// 스레드가 Virtual Thread인지 확인하여 출력
System.out.println("Thread is Virtual? " + virtualThread2.isVirtual());
// ExecutorService 사용
try(final ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 3; i++) {
executorService.submit(runnable);
}
}
# application.yaml
spring:
threads:
virtual:
enabled: true
// Web Request를 처리하는 Tomcat이 Virtual Thread를 사용하여 유입된 요청을 처리하도록 한다.
@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
return protocolHandler -> {
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
}
}
// Async Task에 Virtual Thread 사용
@Bean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
public AsyncTaskExecutor asyncTaskExecutor() {
return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
}
리소스라고 생각하기보다는 Task별로 Virtual Thread 할당
Thread Local 사용시 주의
synchronized 사용시 주의
synchronized 사용시 Virtual Thread에 연결된 Carrier Thread가 Blocking 될 수 있으니 주의
(이런 경우를 pinning 이라고 함)
// synchronized 사용 (pinning 발생)
// 순차적 접근을 보장한다.
public synchronized String accessResource() {
return access();
}
// ReentrantLock 사용 (pinning 발생하지 않음)
private static final ReentrantLock LOCK = new ReentrantLock();
public String accessResource() {
// 순차적 접근을 보장한다.
LOCK.lock();
try {
return access();
} finally {
LOCK.unlock();
}
}
I/O와 관련된 부분에 있어서는 Virtual Thread를 적용했을 때, 상당히 큰 효과를 누릴 수 있다.
Java - Comparable vs Comparator (0) | 2020.08.03 |
---|---|
일급 컬렉션(First Class Collection)의 소개, 써야할 이유 (0) | 2020.05.27 |
J2SE,J2EE의 차이점 (0) | 2020.03.14 |
Java - extends, implements, abstract 차이 (0) | 2020.01.13 |
Java - Equals, Hashcode 메소드 (3) | 2019.12.15 |