Java 19는 게임 체인저다

(원문) https://tomaszs2.medium.com/java-19-is-a-game-changer-21f5f48d239b

Java 언어 사용자에게 축하를 전합니다.

LTS 17 릴리스 이후 새 버전은 다른 언어가 무시하거나 어려움을 겪는 문제에 대한 간단하고 쉬운 솔루션을 제공합니다.

동시성

StructuredTaskScope라는 새로운 형식은 동시성을 처리하는 코드 조각입니다.

동시성에 대한 이러한 접근 방식은 코드가 동작하는 방식을 매우 명확하게 할 뿐만 아니라 코드를 명확하고 읽기 쉽게 만듭니다.

Response handle() throws ExecutionException, InterruptedException {
	try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
		Future<String> user = scope.fork(() -> findUser());
		Future<Integer> order = scope.fork(() -> fetchOrder());

		scope.join();				// 두 fork를 기다린다
		scope.throwIfFailed();		// 에러가 발생한 경우 에러를 전파한다

		// 두 fork가 성공했으므로, 결과를 합친다
		return new Response(user.resultNow(), order.resultNow());
	}
}

좋은 결과지만 가장 중요한 것은 마지막에 설명되어 있습니다.

레코드의 간소화된 조건부 캐스팅

Java 19에서 사용할 수 있는 레코드 패턴 기능 미리보기에는 유형 검사 및 캐스팅을 한 줄로 처리하는 구문이 도입되었습니다.

record Point(int x, int y) {}

void printSum(Object o) {
	if (o instanceof Point(int x, int y)) {
		System.out.println(x + y);
	}
}

또한 이 릴리스에는 Switch의 패턴 일치에 대한 세 번째 프리뷰가 포함되어 있습니다.

이러한 변화는 놀랍지만 마지막 변화는 실제입니다.

가상 스레드

다른 운영 체제의 스레드는 주의가 필요하고 많은 리소스를 소비합니다.

Modern Java는 서버에서 동시성을 크게 향상시킬 수 있는 대안을 미리 보여줍니다.

이를 가상 스레드라고 합니다.

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
	IntStream.range(0, 10_000).forEach(i -> {
		executor.submit(() -> {
			Thread.sleep(Duration.ofSecond(1));
			return i;
		});
	});
}	// executor.close()는 내부적으로 호출되고, 종료를 기다린다

Java 19의 기타 변경 사항:

  • 외부 기능 및 스토리지 API 미리보기
  • Vector API의 네 번째 인큐베이션

(더 알아보기) https://www.infoworld.com/article/3653331/jdk-19-the-new-features-in-java-19.html


콘텐츠 추가

구조화된 동시성

먼저 이 텍스트의 예제를 구조화된 동시성을 사용하지 않는 코드로 변경해 보겠습니다.

Response handle() throws ExecutionException, InterruptedException {
	try (ExecutorService executorService = Executors.newFixedThreadPool(2)) {
		Future<String> user = executorService.submit(() -> findUser());
		Future<Integer> order = executorService.submit(() -> fetchOrder());

		String userValue = user.get();
		Integer orderValue = order.get();

		return new Response(userValue, orderValue);
	}
}

언뜻 보기에는 문제가 없어 보이지만 문제는 사용자나 주문을 실행할 때 예외가 발생했을 때입니다.

user.get()을 기다리는 동안 예외가 발생하면 사용자의 진행을 중지하고 빠르게 종료하는 것이 효율적입니다.

그러나 위의 코드는 user.get()이 반환될 때까지 기다린 다음 order.get()에서 예외를 throw합니다.

핸들 방식을 실행하는 스레드가 종료되더라도 사용자와 주문은 계속 실행됩니다.

이러한 예외가 발생하면 텍스트에 표시된 대로 구조화된 동시성을 사용하여 예외를 전파하고 빠르게 종료할 수 있습니다.

(참조) https://www.baeldung.com/java-structured-concurrency

스위치 하우징

Java 17에서는 하위 클래스 유형별로 일치시킬 수 있습니다.

또한 아래 예와 같이 o는 자동으로 i와 l로 변환됩니다.

public String match(Object o) {
	return switch(o) {
		case Integer i -> "Integer value = " + i;
		case Long l -> "Long value = " + l;
		default -> "Not Integer";
	};
}

이 텍스트의 예와 유사하게 데이터 레코드도 전환 사례와 일치시킬 수 있으며 조건은 when과 함께 추가할 수 있습니다.

record Point(int x, int y) {}

public String diamond(Point p) {
	return switch(p) {
		case Point(int x, int y) when x + y > 10 -> "Outside";
		case Point(int x, int y) -> "Inside";
		default -> "On the line";
	};
}

가상 스레드

기존 Java 스레드(플랫폼 스레드)는 OS 커널 스레드와 1:1로 정렬됩니다.

Java 19의 새로운 기능인 가상 스레드는 플랫폼 스레드와의 다대다 관계입니다.

적은 수의 플랫폼 스레드에서 실행되는 여러 가상 스레드를 예약합니다.

요청당 하나의 스레드를 할당하는 서버 애플리케이션에서 처리량을 늘릴 수 있습니다.


가상 스레드와 플랫폼 스레드 간의 관계

(참조) https://medium.com/javarevisited/how-to-use-java-19-virtual-threads-c16a32bad5f7

외부 기능 및 스토리지 API

Java 런타임 외부의 코드와 상호 작용할 수 있게 해주는 API입니다.

JNI의 단점을 보완하고 외부 기능 및 외부 저장소에 대한 안전하고 쉬운 액세스를 허용합니다.


(참조) https://openjdk.org/jeps/434

벡터 API

CPU의 SIMD(Single Instruction, Multiple Data)를 이용하여 빠른 벡터 연산을 가능하게 하는 API입니다.

내부 구현은 CPU의 아키텍처에 따라 다르지만 사용하기 쉽도록 통합 API로 제공됩니다.

(참조) https://medium.com/javarevisited/how-to-use-java-19-virtual-threads-c16a32bad5f7