2022년도 Java의 새로운 기능 19가지
2022년 9월 20일 자바 19가 출시된다. 그것은 몇 가지 흥미로운 새로운 특징들을 포함하고 있다. 가장 주목할 만한 것은 레코드에 대한 패턴 매칭과 가상 스레드의 첫 번째 미리 보기 버전 및 구조화된 동시성입니다. 이 짧은 기사에서는 이러한 변경 사항과 기타 모든 향상된 기능에 대해 설명하겠습니다.
자바 19는 7개의 이른바 자바 강화 제안서(JEP)로 구성되어 있다. 다음과 같습니다.
- 405 레코드 패턴
- 422 Linux/RISC-V 포트
- 424 외부 기능 및 메모리 API
- 425개의 가상 스레드
- 426 벡터 API
- 427 스위치 패턴 매칭
- 428 구조화 동시성
레코드 패턴
레코드는 자바 14에 도입되었고 즉시 히트를 쳤다. 리프레셔로서, 레코드는 불변적이고 자바빈 규약을 따르지 않는 클래스 같은 객체이다. 다음은 한 가지 예입니다.
우리는 인스턴스 변수에 접근하기 위해 다음과 같은 코드를 작성합니다.
여기서 우리가 다루고 있는 객체의 유형을 식별하기 위해 패턴 일치를 사용하는 프로세스 방법에 초점을 맞추고 있다(19줄과 22줄). 유형 패턴 일치를 사용하면 암시적 개체 캐스트가 필요하지 않습니다.
그러나 레코드에 대한 패턴 일치는 한 단계 더 나아갑니다. 위의 예에서 우리는 직원 및 계약자 객체에 관심이 없고 이름, 부서 및 수수료와 같은 속성에 관심이 있습니다.
레코드 패턴은 해체를 위한 매개 변수 이름을 지정할 수 있도록 허용함으로써 이 작업을 수행합니다.
2행과 5행의 예에 이어 직원과 계약자의 전체 구조를 어떻게 설명하는지 주목하십시오. 변수 이름이 레코드 정의 중에 사용된 이름과 같을 필요는 없습니다.
중첩된 해체 또한 지원되므로 다른 레코드로 초기화된 레코드는 이러한 방식으로 해체될 수 있다.
Linux/RISC-V 포트
이 JEP는 JDK를 RISC-V 명령 집합 아키텍처로 이식하여 Java를 이 플랫폼에서 컴파일하고 실행할 수 있도록 하는 것입니다.
외부 함수
Panama 프로젝트에서 유래한 이 JEP는 Foreign Memory Access API와 Foreign Linker API라는 두 가지 JEP를 결합합니다. 그것들은 과거에 별도로 도입되었고 Foreign Function and Memory API로 통합되었다. 처음에는 JDK 17에서 인큐베이팅 API로 소개되었지만, 현재는 미리보기 API이다.
이것의 목표는 자바 런타임의 통제 밖에 있는 메모리에 대한 더 나은 메모리 관리와 자바 내부의 네이티브 라이브러리와의 더 나은 통합이다.
이 JEP의 변경 사항은 경미하며 주로 이 API의 이전 버전에 대한 사용자 피드백을 기반으로 합니다.
가상 스레드
이것은 JVM에 가상 스레드를 제공하는 Project Loom의 결과의 첫 번째 버전입니다. 가상 스레드는 운영 체제 스레드에 의존하는 플랫폼 스레드와 달리 Java 런타임에 의해 제어되는 스레드입니다.
그럼 어떻게 작동하는 걸까요? 먼저 현재 스레드를 살펴보겠습니다. 운영 체제 스레드와 1:1 관계입니다. 운영 체제 스레드는 시간과 리소스 소비 모두에서 생성 비용이 많이 듭니다. 그렇기 때문에 리소스가 부족하기 전에 많은 리소스를 보유할 수 있습니다. 또한 플랫폼 스레드가 차단되면 운영 체제 스레드가 차단됩니다. 차단 기간 동안 운영 체제 스레드에서 다른 코드를 실행할 수 없습니다.
이 문제를 피하기 위해 개발자들은 반응형 프로그래밍에 손을 뻗었다. 그러나 그것은 그 자체의 문제를 가져온다: 그 코드는 거의 틀림없이 읽고 유지하기가 더 어렵고 디버깅하기도 더 어렵다. 자바가 어떻게 그 문제를 해결하려고 시도하는지에 대한 구조화된 동시성에 대한 절을 참조하십시오.
반면에 가상 스레드는 운영 체제 스레드와 n:m 관계를 갖는다. 가상 스레드를 실행할 수 있으면 운영 체제 스레드에 배치되며, 일단 가상 스레드가 차단되면 운영 체제 스레드에서 제거되고 다른 스레드가 o/s 스레드에 배치됩니다. 스레드들은 일반적으로 입출력 작업을 차단하며, 이 경우 자바 런타임은 단순히 I/O 작업을 대체한다. 차단 작업이 완료되면 가상 스레드를 다시 실행할 수 있습니다. Java 가상 스레드 스케줄은 동일한 운영 체제 스레드에 배치하도록 선택할 수 있지만, 이것은 보장되지 않습니다. 따라서 n:m 관계는 여러 운영 체제 스레드에서 얼마든지 실행할 수 있다.
가상 스레드는 생성하는 데 매우 저렴하고 더 적은 리소스를 필요로 하므로 많은 리소스를 사용할 수 있습니다. 실제로 1,000개, 10,000개 또는 1,000.000개를 사용할 수 있지만 일반적인 시나리오에서는 수백 개의 플랫폼 스레드를 사용할 수 있습니다. 이러한 이유로, 플랫폼 스레드는 일반적으로 재사용을 위해 풀링되는 반면, 가상 스레드는 풀링되지 않아야 합니다.
그렇다면 이것은 가상 스레드를 생성하는 것이 우리가 자바에서 스레드를 생성하는 데 사용하는 방식과 완전히 다르다는 것을 의미합니까? 걱정 마, 다르지 않아. 사실, 가상 스레드를 사용할 때 거의 변하지 않을 것이다. 풀링은 더 이상 필요하지 않거나 권장되지 않습니다.
아래는 가상 스레드와 플랫폼 스레드를 모두 생성하는 방법에 대한 코드 조각입니다.
보시다시피, 다른 방법을 사용하여 작성할 쓰레드 유형을 나타냅니다. 그 이후로, 그것들을 사용하는 방법에는 차이가 없습니다. Tomcat 또는 Jetty와 같은 웹 서버는 사용할 때 다른 느낌으로 느껴지지 않을 것입니다. 오라클은 현재 가상 스레드를 사용하는 자체 마이크로 서비스 프레임워크 헬리돈의 새로운 버전을 개발하고 있다. 명확한 이유(가상 스레드는 Java 19의 미리 보기 버전)로 인해 아직 프로덕션 용도로 릴리스되지 않았지만 스핀을 제공할 수 있습니다. 자세한 내용은 이 문서를 참조하십시오.
실행자를 사용하려면 새 실행자 newVirtual이 있습니다.가상 스레드를 생성하기 위한 ThreadPerTaskExecutor입니다.
가상 스레드는 엄청난 개선을 가져오지만 현재 모든 사용 사례를 대상으로 하는 것은 아닙니다. 특히 CPU를 많이 사용하는 작업은 블록 수를 줄이는 경향이 있기 때문에 향상된 확장성의 이점을 별로 누릴 수 없습니다. 또한 동기화된 블록을 많이 사용하여 코드에 대한 동시 접근을 제한하는 코드를 대신 재진입 잠금으로 다시 작성해야 한다는 점도 언급할 가치가 있다.
벡터 API
이것은 벡터 API의 네 번째 배양이다. 크기 벡터 유형에 대한 데이터 병렬 연산을 위한 일련의 방법을 소개한다. 이 버전의 변경 사항에는 JEP 424에 정의된 대로 메모리 세그먼트에 벡터를 로드하고 저장하기 위한 API가 포함되어 있습니다. 또한 보완 벡터 마스크 압축 작업과 함께 두 가지 새로운 교차 차선 벡터 연산인 압축과 역확장을 추가한다. 그리고 마지막으로 지원되는 비트별 적분 차선 연산 세트를 확장한다. 이 API에 대한 자세한 내용은 이 글과 이 글을 참조하시기 바랍니다.
스위치에 대한 패턴 일치
이것은 두 가지 향상된 기능과 함께 제공되는 스위치 패턴 매칭의 세 번째 미리보기입니다. 우선, 보호 패턴은 스위치 블록의 절이 있을 때 로 대체된다.
이에 대해 좀 더 설명하자면, 보호 패턴을 사용하는 아래의 코드를 보십시오(4줄).
Java 19에서 4행은 다음과 같이 작성됩니다.
두 번째 변화는 선택기의 값이 null일 때, 의미론은 이제 레거시 스위치 의미론과 더 밀접하게 정렬된다. 이것은 null 값이 명시적으로 처리되지 않을 때 컴파일러가 NullPointer 예외를 던지는 코드를 삽입한다는 것을 의미합니다. 3행의 위의 코드에서처럼 null을 처리하거나 컴파일러에게 Null 포인터를 던지는 고전적인 동작으로 되돌리는 옵션을 남겨줍니다.예외(NPE).
즉, 이 코드(null 점검이 없음을 알 수 있음)
이 코드와 동일합니다(3줄의 null 점검).
구조화 동시성
구조화된 동시성은 다중 스레드 및 병렬 프로그래밍을 단순화하는 것을 목표로 하는 인큐베이터 JEP이다. JDK 개발자들은 동시 프로그래밍을 사용하는 현재의 작업 방식에 결함이 있다는 것을 인식했다.
먼저 예를 살펴보겠습니다.
이 접근 방식에는 여러 가지 단점이 있습니다.
- 세 개의 스레드 중 하나가 예외를 발생시키면 다른 스레드들은 결과가 사용되지 않더라도 계속 실행됩니다. 특히 장기간 실행되는 프로세스의 경우 이는 시간과 자원의 낭비일 수 있습니다.
- RetrieveCustomerInfo 스레드가 취소된 경우에도 하위 스레드는 계속 실행됩니다.
- 별도의 스레드가 스레드 덤프에 관련 없는 스레드로 나타나기 때문에 디버깅이 더 복잡합니다.
구조화된 동시성 이면의 아이디어는 실행의 주 스레드와 하위 스레드의 관계가 있다는 것이다. 즉, 메인 스레드가 취소되거나 하위 스레드가 예외를 발생시키면 모든 스레드가 취소되어 원래 예에서 발생한 스레드가 누출되지 않습니다.
구조화된 동시성을 사용하면 위의 코드를 다음과 같이 다시 작성할 수 있습니다.
여기서는 Structured Task Scope를 사용하고 있습니다. 보다 정확하게는 하나의 스레드가 실패할 경우 모든 스레드를 취소하는 구현입니다. 조인 메서드는 모든 스레드가 실행될 때까지 기다립니다. 범위가 AutoClose 가능하므로 완료 또는 실패 시 리소스를 확보하기 위해 자동으로 닫힙니다.
사용할 수 있는 구조화 작업 범위에는 여기서 사용되는 ShutdownOnFailure와 ShutdownOnSuccess의 두 가지 구현이 있습니다. 마지막 스레드는 성공적으로 완료된 첫 번째 스레드의 결과를 반환하고 나머지 스레드는 취소합니다. 이 구현은 둘 이상의 원본에서 동일한 데이터를 검색하려는 경우에 유용합니다.
구조적 동시성은 가상 스레드와 함께 작동하며, 이는 구조적 작업 범위에 의해 생성되는 스레드가 가상 스레드가 된다는 것을 의미합니다.
반응형 프로그래밍에 비해 이 접근 방식은 읽고 디버깅하는 것이 훨씬 쉽다.
구조화된 동시성에 대해 더 읽고 싶다면 내 동료인 David Vlijmincx의 이 훌륭한 기사를 읽어보세요.
결론
Java 19는 미래에 대한 큰 전망과 함께 몇 가지 큰 개선 사항을 제공합니다. 6개월간의 증가분은 자바 언어와 생태계를 빠르게 발전시키겠다는 약속에 부응한다. 가서 다운받아서 한번 해보세요.