Home Java 21의 새로운 기능
Post
Cancel

Java 21의 새로운 기능

Java 21 2023년 9월에 출시된 자바 LTS 버전입니다. Java 21은 다양한 새로운 기능과 개선사항을 제공합니다. 이번 릴리스는 특히 성능 향상, 언어 업데이트 및 개선, 그리고 Project Loom과 Project Panama의 도입에 중점을 두고 있습니다.

Oracle Java SE Support Roadmap1

ReleaseGA DatePremier Support UntilExtended Support UntilSustaining Support
8 (LTS)March 2014March 2022December 2030Indefinite
9 - 10 (non-LTS)September 2017 - March 2018March 2018 - September 2018Not AvailableIndefinite
11 (LTS)September 2018September 2023January 2032Indefinite
12 - 16 (non-LTS)March 2019 - March 2021September 2019 - September 2021Not AvailableIndefinite
17 (LTS)September 2021September 2026September 2029Indefinite
18 (non-LTS)March 2022September 2022Not AvailableIndefinite
19 (non-LTS)September 2022March 2023Not AvailableIndefinite
20 (non-LTS)March 2023September 2023Not AvailableIndefinite
21 (LTS)September 2023September 2028September 2031Indefinite
22 (non-LTS)March 2024September 2024Not AvailableIndefinite
23 (non-LTS)September 2024March 2025Not AvailableIndefinite
24 (non-LTS)March 2025September 2025Not AvailableIndefinite
25 (LTS)September 2025September 2030September 2033Indefinite

Project Loom

Project Loom이란?

  • “The goal of this Project is to explore and incubate Java VM features and APIs built on top of them for the implementation of lightweight user-mode threads (fibers), delimited continuations (of some form), and related features, such as explicit tail-call.”2
    • “이 프로젝트의 목표는 경량 사용자 모드 스레드(fiber), 일부 형태의 구분된 연속성(delimited continuations) 및 명시적 꼬리 호출과 같은 관련 기능을 구현하기 위한 Java VM 기능과 API를 탐색하고 육성하는 것입니다.”
  • “Project Loom is to intended to explore, incubate and deliver Java VM features and APIs built on top of them for the purpose of supporting easy-to-use, high-throughput lightweight concurrency and new programming models on the Java platform.”3
    • “Project Loom은 Java 플랫폼에서 쉽게 사용할 수 있는 고처리량의 경량 동시성을 지원하고, 이를 위한 새로운 프로그래밍 모델을 탐색, 육성 및 제공하기 위해 그 위에 구축된 Java VM 기능과 API를 목표로 합니다.”
  • 가상 스레드(JEP 444): Java의 병렬 처리 및 비동기 프로그래밍 기능을 향상시키는 데 중요한 역할을 하는 경량 쓰레드(lightweight threads)입니다. 참고: Java의 미래, Virtual Thread by 우아한기술블로그

  • 스코프 지정 값(Scoped Values, JEP 446, 프리뷰): Scoped Values는 특정 스레드 또는 작업의 실행 컨텍스트 내에서만 유효한 불변 데이터를 관리하는 데 사용됩니다. 스레드 내부 및 스레드 간 불변 데이터 공유를 활성화하고, 이를 통해 개발자는 복잡한 멀티스레딩 환경에서 프로젝트의 사용 편의성, 이해성, 견고성 및 성능을 높이는 데 도움이 됩니다.

    • ScopedValue의 주요 특징
      1. 데이터 불변성: 한 번 설정되면, ScopedValue의 값은 해당 스코프 내에서 변경되지 않습니다. 이는 데이터의 안정성과 예측 가능성을 증가시킵니다.
      2. 스레드 안전성: ScopedValue는 스레드 간 데이터 공유 시 안전성을 제공합니다. 각 스레드는 독립적인 값을 가질 수 있어 데이터 충돌의 위험이 줄어듭니다.
      3. 자동적인 생명 주기 관리: ScopedValue는 스코프의 시작과 끝에 따라 자동으로 관리됩니다. 이는 자원 누수를 방지하고 메모리 관리를 간소화합니다.
      4. 선언적 스코프 사용: 데이터를 명시적으로 스코프 내에서만 사용하도록 함으로써, 코드의 가독성과 유지보수성을 향상시킵니다.
      5. 멀티스레딩 환경 최적화: 멀티스레딩 환경에서 복잡한 데이터 공유 문제를 해결하고, 성능을 최적화합니다
    • 예제 코드

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      
      public class SharedContextExample {
          private static final ScopedValue<String> CONTEXT_VALUE = ScopedValue.newInstance();
      
          public void run() {
              String sharedValue = "sharedData";
              for(int i = 0; i < 10; i++) {
                  String value = sharedValue + i;
                  Thread t = new Thread(() -> ScopedValue.runWhere(CONTEXT_VALUE, value, SharedContextExample::doWork));
                  t.start();
              }
          }
      
          private static void doWork() {
              String value = CONTEXT_VALUE.get();
              System.out.println("Value in thread: " + value);
          }
      }
      
  • 구조화된 동시성(Structured Concurrency, JEP 453, 프리뷰): Structured Concurrency는 동시에 실행되는 여러 작업(쓰레드)을 더 명확하고 관리하기 쉬운 구조로 만들기 위해 설계되었습니다. 복잡한 멀티스레드 애플리케이션의 개발과 유지 보수를 단순화하고, 관련 위험을 줄여줍니다.

    • Structured Concurrency의 주요 특징
      1. 구조화된 쓰레드 블록(Structured Thread Blocks): Structured Concurrency는 코드 내에서 명시적으로 구조화된 블록을 사용하여 쓰레드를 관리합니다. 이는 프로그래머가 쓰레드의 생성과 종료를 더 명확하게 제어할 수 있게 해 줍니다.
      2. 자원 관리와 오류 처리의 개선: 전통적인 쓰레드 관리 방식에서는 종종 쓰레드가 예기치 않게 종료되거나 자원을 제대로 정리하지 않는 문제가 발생했습니다. Structured Concurrency는 이러한 문제를 해결하기 위해 쓰레드의 생명주기와 자원 사용을 더 잘 관리할 수 있도록 합니다.
      3. 코드의 가독성과 유지 보수성 향상: 쓰레드의 생성과 관리가 명확한 구조 내에서 이루어지므로, 코드의 가독성이 좋아지고 유지 보수가 쉬워집니다. 이는 복잡한 동시성 문제를 해결하는 데 도움이 됩니다.
      4. 오류와 예외 처리의 용이성: 구조화된 블록 내에서 발생하는 예외를 더 쉽게 관리하고 처리할 수 있습니다. 이는 프로그램의 안정성을 높이는 데 기여합니다.
      5. 성능의 최적화: 효율적인 쓰레드 관리는 애플리케이션의 전반적인 성능을 향상시킬 수 있습니다. 구조화된 접근 방식은 쓰레드의 오버헤드를 줄이고, 자원 사용을 최적화합니다.
    • 예제 코드

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      
      import java.util.concurrent.*;
      
      public class StructuredConcurrencyExample {
          public void run() {
              // Structured Concurrency 블록을 사용합니다.
              try (var scope = new StructuredTaskScope<Void>()) {
                  // 여러 개의 비동기 작업을 시작합니다.
                  var task1 = scope.fork(() -> performTask("Task 1"));
                  var task2 = scope.fork(() -> performTask("Task 2"));
                  var task3 = scope.fork(() -> performTask("Task 3"));
      
                  try {
                      // 모든 작업이 완료될 때까지 기다립니다.
                      scope.join();
                  } catch (InterruptedException e) {}
              }
          }
      
          private static Void performTask(String taskName) {
              System.out.println(taskName + " is running");
              return null;
          }
      }
      

성능 업데이트

  • 세대별 ZGC(JEP 439): Generational ZGC는 기존의 Z Garbage Collector에 세대별 가비지 컬렉션 기능을 추가하여 성능을 향상시킨 것입니다. 여기서 세대별 가비지 컬렉션이란, 객체를 젊은 세대(Young Generation)늙은 세대(Old Generation)로 구분하고, 각 세대에 맞는 최적화된 가비지 컬렉션을 수행하는 방식을 말합니다.

    • Generational ZGC의 특징
      1. 젊은 세대와 늙은 세대의 분리: 객체는 생성 초기에 젊은 세대에 할당되고, 일정 시간 생존한 후에는 늙은 세대로 이동합니다. 이를 통해 더 효율적인 메모리 관리가 가능합니다.
      2. 효율적인 메모리 관리: 젊은 세대에서는 객체가 빠르게 생성되고 소멸되는 특성을 고려하여 더 빠른 가비지 컬렉션을 수행합니다. 늙은 세대에서는 객체가 더 오래 존재하기 때문에 덜 자주, 하지만 더 철저한 가비지 컬렉션을 수행합니다.
      3. 낮은 대기 시간(Low Latency): Generational ZGC는 ZGC의 핵심 장점인 낮은 대기 시간을 유지하면서 메모리 관리 효율을 높입니다. 이는 특히 대용량 메모리를 사용하는 애플리케이션에 유리합니다.
      4. 동적 메모리 할당: 메모리 사용량에 따라 동적으로 젊은 세대와 늙은 세대의 크기를 조절할 수 있습니다. 이는 시스템 리소스의 유연한 관리를 가능하게 합니다.
      5. 멀티 스레드 가비지 컬렉션: Generational ZGC는 멀티 스레드를 활용하여 가비지 컬렉션을 병렬로 수행합니다. 이는 가비지 컬렉션의 효율을 높이고, 애플리케이션의 응답 시간을 개선합니다.

Generational ZGC의 도입은 Java 애플리케이션의 성능과 효율성을 크게 향상시킬 것으로 기대됩니다. 특히, 대규모 데이터를 처리하는 애플리케이션 또는 실시간 서비스에서의 이점이 클 것으로 예상됩니다.

언어 업데이트 및 개선

  • 문자열 템플릿(String Templates, JEP 430, 프리뷰): 런타임에서 계산된 값을 포함하는 문자열 표현을 단순화합니다.
    • 예제 코드

      1
      2
      3
      
      String name = "June";
      int age = 25;
      String message = STR."Hello, my name is \{name} and I'm \{age} years old.";
      
  • 레코드 패턴(Record Patterns, JEP 440, 세 번째 프리뷰): 레코드 클래스 인스턴스의 해체를 위한 패턴 매칭을 확장합니다.
    • 예제 코드

      1
      2
      3
      4
      5
      6
      7
      8
      9
      
      public record Person(String name, int age) {}
      
      public static void main(String[] args) {
        Object obj = new Person("June", 25);
      
        if (obj instanceof Person(String name, int age)) {
          System.out.println("Name: " + name + ", Age: " + age);
        }
      }
      
  • 스위치 패턴 매칭(Pattern Matching for Switch, JEP 441): 스위치 문의 표현력과 적용성을 확장합니다.

    • 예제 코드

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      
      public static void main(String[] args) {
        Object obj = "Hello";
      
        String result = switch (obj) {
          case Integer i -> System.out.println("Integer: " + i);
          case String s -> System.out.println("String: " + s);
          case null -> System.out.println("It's null!");
          default -> System.out.println("Other type");
        }
      
        System.out.println(result);
      }
      
  • 이름 없는 패턴 및 변수(Unnamed Patterns and Variables, JEP 443, 프리뷰): 레코드 패턴의 가독성을 향상시킵니다.
    • Unnamed Patterns (이름 없는 패턴)
      • 패턴 매칭에서 레코드 구성 요소의 유형과 이름을 생략할 수 있어 코드의 가독성이 향상됩니다.
      • 예제 코드

        1
        2
        3
        4
        5
        6
        
        record ColoredPoint(Point point, Color color) {}
        
        var r = new ColoredPoint(new Point(1, 2), new Color(255, 0, 0));
        if (r instanceof ColoredPoint(Point p, _)) {
          System.out.println("Point: " + p);
        }
        
    • Unnamed Variables (이름 없는 변수)
      • 변수를 선언해야 하지만 그 값은 사용되지 않는 상황에서 유용합니다.
      • 예제 코드

        1
        2
        3
        4
        5
        6
        7
        8
        
        int LIMIT = 100;
        int acc = 0;
        for (Order _ : orders) {
            if (acc < LIMIT) { 
                acc++;
            }
        }
        System.out.println("acc: " + acc);
        
      • 목적과 이점
        • 간결성: 더 간결하고 읽기 쉬운 코드를 작성할 수 있습니다.
        • 효율성: 코드의 효율성을 높이고, 오류 가능성을 줄일 수 있습니다.
  • 이름 없는 클래스 및 인스턴스 메인 메소드(Unnamed Classes and Instance Main Methods, JEP 445, 프리뷰): 업데이트는 Java 프로그래밍 언어를 더욱 접근하기 쉽고 초보자 친화적으로 만드는 데 중요한 역할을하여 JAVA 프로그래밍 개념을 점진적으로 소개하는데 도움을 줍니다.

    • Unnamed Classes (이름 없는 클래스)
      • Java 21에서는 클래스를 선언하지 않고도 프로그램을 작성할 수 있습니다. 이는 특히 간단한 프로그램을 작성할 때 유용합니다.
      • 이름 없는 클래스는 이름이 없으며 다른 클래스에서 호출할 수 없습니다. 이러한 클래스는 메소드와 필드를 포함할 수 있으며, 주로 프로그램의 진입점으로 사용되는 main() 메소드를 포함합니다.
      • 이름 없는 클래스는 암시적으로 상위 클래스로 간주되며, 이들은 final이며 Object 클래스 외에 다른 클래스를 확장하거나 인터페이스를 구현할 수 없습니다.45
    • Instance Main Methods (인스턴스 메인 메소드)
      • 이전의 Java 버전에서는 프로그램의 진입점으로 public static void main(String[] args) 메소드가 필요했습니다. 하지만 Java 21부터는 더 유연한 진입점 메소드를 사용할 수 있습니다.
      • 새로운 프로토콜은 main() 메소드를 public, protected, 또는 기본 (패키지) 접근 수준으로 선언할 수 있게 하며, static이 아니어도 되고 String[] 매개변수를 필요로 하지 않습니다.
      • 이러한 변경을 통해 간단한 “Hello, World!” 프로그램을 작성할 때 클래스 선언, 접근 수정자, static 키워드를 사용할 필요가 없어졌습니다.467
    • Launch Protocol (런칭 프로토콜)
      • 클래스를 실행할 때 런칭 프로토콜은 다음 메소드 중 하나를 선택하여 호출합니다: static void main(String[] args), static void main(), void main(String[] args) 인스턴스 메소드, 또는 void main() 인스턴스 메소드.
      • 이러한 변경은 클래스가 상속받은 “전통적인” public static void main(String[] args) 메소드 대신 인스턴스 메인 메소드를 호출하도록 합니다4.
    • 예제 코드
      • 기존 Java 메인 메소드 방식
      1
      2
      3
      4
      5
      
      public class HelloWorld {
          public static void main(String[] args) {
              System.out.println("Hello, World!");
          }
      }
      
      • Java 21의 이름 없는 클래스와 인스턴스 메인 메소드 방식
      1
      2
      3
      
      void main() {
          System.out.println("Hello, World!");
      }
      

Project Panama

Project Panama이란?

  • “We are improving and enriching the connections between the Java virtual machine and well-defined but “foreign” (non-Java) APIs, including many interfaces commonly used by C programmers.”8
    • “우리는 자바 가상 머신과 명확하게 정의되었지만 “외부의” (비자바) API들, 특히 C 프로그래머들이 흔히 사용하는 많은 인터페이스들 사이의 연결을 개선하고 풍부하게 만들고 있습니다.”
  • 외부 기능 및 메모리 API(Foreign Function & Memory API, JEP 442, 세 번째 프리뷰): 이 API의 주요 목적은 Java 언어와 네이티브 코드 간의 상호 운용성을 개선하는 것입니다. 이를 통해 Java 개발자들은 네이티브 라이브러리를 더 쉽고 안전하게 사용할 수 있게 되며, Java와 C 또는 다른 네이티브 언어 사이의 경계를 더 효율적으로 넘나들 수 있게 됩니다.

    • Foreign Function API
      • 목적: Java에서 네이티브 함수를 호출할 수 있게 해주는 것이 주된 목적입니다. 기존의 JNI(Java Native Interface)보다 사용하기 쉽고, 안전합니다.
      • 기능
        • 네이티브 함수의 안전하고 효율적인 호출을 지원합니다.
        • 함수 포인터를 사용하여 네이티브 함수에 접근하고, 이를 Java 메서드와 연결합니다.
        • 네이티브 함수의 시그니처를 Java에서 표현하고, 자동으로 변환을 처리합니다.
    • Memory API
      • 목적: 안전하고 효율적인 방법으로 네이티브 메모리에 접근하고 관리할 수 있도록 하는 것입니다.
      • 기능
        • 네이티브 메모리 할당, 접근 및 해제를 위한 API를 제공합니다.
        • 기존의 Unsafe API보다 더 안전하고 쉬운 메모리 관리를 가능하게 합니다.
        • 메모리 레이아웃 및 메모리 세그먼트 개념을 도입하여, 메모리 구조를 명확하게 표현하고 관리합니다.
    • 중요성
      • 성능 향상: 네이티브 코드와의 더 나은 통합으로 인해 성능이 향상될 수 있습니다.
      • 안전성 증가: 기존의 JNI 방식보다 더 안전하게 네이티브 코드를 다룰 수 있어, 오류와 보안 취약점을 줄일 수 있습니다.
      • 편의성 개선: 네이티브 코드와의 상호 작용이 더 쉽고 직관적으로 이루어질 수 있어 개발자의 작업 부담이 감소합니다.
  • 벡터 API(Vector API, JEP 448, 여섯 번째 인큐베이터): Vector API는 Java 언어에 SIMD (Single Instruction, Multiple Data) 연산을 지원하기 위해 설계된 새로운 기능입니다. SIMD는 여러 데이터 요소에 동일한 연산을 병렬로 수행함으로써 성능을 향상시키는 기술입니다. 이는 특히 과학 계산, 이미지 처리, 기계 학습 등 데이터 집약적 작업에 유용합니다.

This post is licensed under CC BY 4.0 by the author.

아파치 카프카(Apache Kafka)란? 구조와 특징

Spring Batch 5