Nine New Developer Features in JDK 9


1. Factory Methods for Collections (JEP 269 : JDK Enhancement Proposals)

List, Set, and Map

=> JDK 9 이전에는 미리 정의된 데이터들과 콜렉션을 생성하는 쉬운 방법을 제공하지 않습니다.
=> 위의 행위에 대해 Factory Methods 를 사용하면 더욱 편리하게 코드를 작성 할 수 있습니다.

In JDK 8,

List<Point> myList = new ArrayList();
myList.add(new Point(1, 1));
myList.add(new Point(2, 2));
myList.add(new Point(3, 3));
myList.add(new Point(4, 4));
myList = Collections.unmodifiableList(myList);

In JDK 9,

List<Point> list = List.of(new Point(1, 1), new Point(2, 2), new Point(3, 3), new Point(4, 4));

Set<String> set = Set.of("a", "b", "c");

Map<k-type, v-type> map= Map.of()
                         Map.of(k1, v1)
                         Map.of(k1, v1, k2, v2)
                         Map.of(k1, v1, k2, v2, k3, v3)
                         ...

Map<k-type, v-type> map = Map.ofEntries(
    Map.entry(k1, v1),
    Map.entry(k2, v2),
    Map.entry(k3, v3),
    // ...
    Map.entry(kn, vn));

특징 :

  1. Set을 만들 때 중복 인수를 전달할 수 없으며 Map을 만들 때 중복 키를 전달할 수도 없습니다.
  2. Null 값은 컬렉션 팩토리 메서드에 대한 값으로 사용할 수 없습니다.
  3. Javadoc 문서는 메소드 호출 방법에 대한 전체 설명을 제공합니다.
    컬렉션은 "Java 9 핵심 라이브러리 업데이트 : 콜렉션 및 스트림" 기사에서 더 자세히 설명됩니다.

2. Optional Class Enhancements

Optional Class In Java 9

참조 : https://aboullaite.me/java-9-enhancements-optional-stream/

Java 9에서는 Optional Class에 대해 새로운 메서드 들이 추가되었습니다.

ifPresent(Consumer action)
: 값이 현존한다면 값을 이용하여 액션을 수행합니다.

// Using Java 8
if (OptionalObj.ifPresent())
    ...

// Using Java 9
OptionalObj.ifPresent(() -> action());

ifPresentOrElse(Consumer action, Runnable emptyAction)
: ifPresent와 유사합니다. 하지만 값이 empty일때 emptyAction을 수행합니다.

// Using null check:
Student student = getStudentByIdNo(studentIdNo);
if (student != null)
    displayStudentInfo(student);
else
    displayStudentNotFound();

// Using the new Java 9 Optional::ifPresentOrElse:
getStudentByIdNo(studentIdNo).ifPresentOrElse(this::displayStudentInfo, ()-> displayStudentNotFound());

or(Supplier supplier)
: 이 메서드는 언제든지 Optional이 항상 있는지 확인하려는 경우에 유용합니다. 값이 있으면 값을 설명하는 Optional을 반환하고 그렇지 않으면 supplier에 의해 생성된 Optional을 반환합니다.

public Optional<Student> getStudentByIdNo(String studentIdNo) {
    return findInCurrentCollegeYear(studentIdNo).or(() -> findInArchive(studentIdNo));
}

stream()
: 값의 유무에 따라 구성된 스트림을 반환합니다. empty 체크를 해서 자동으로 해당 element를 삭제합니다.

// streamOptional(): [(Optional.empty(), Optional.of("one"), Optional.of("two"), Optional.of("three")]

// Using Java 8
List<String> strings = streamOptional()
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toList());

// Result: strings[one, two, three]



// Using Java 9
List<String> newStrings = streamOptional()
                .flatMap(Optional::stream)
                .collect(Collectors.toList());

// Result: newStrings[one, two, three]

3. Stream API Enhancements

Stream API는 콜렉션으로부터 스트림을 만들 수 있게 해줍니다. 또한 JDK 8에서는 Collection API 외적으로 몇몇 메서드를 지원하고 있습니다. JDK 9에서는 java.util.Scanner와 java.util.regex.Matcher 같은 새로운 기능을 추가했습니다.

첫째로 takeWhile(Predicate)와 dropWhile(Predicate) 두 개의 메서드입니다. 이 메서드들은 기준의 limit()과 skip() 메서드들과 기능이 일치합니다. 하지만 이 메서드들은 fixed integer value 보다 Predicate를 사용합니다.

  1. takeWhile () 메서드는 계속해서 입력 스트림에서 요소를 가져 와서 조건부의 메서드가 true를 반환 할 때까지 출력 스트림에 전달합니다.

  2. dropWhile () 메소드는 그 반대입니다. 조건부의 메서드가 false를 돌려 줄 때까지, 입력 스트림로부터 요소를 삭제합니다. 입력 스트림의 나머지 모든 요소는 출력 스트림으로 전달됩니다.

이 메서드들을 사용할 때는 정렬되지 않은 스트림에 대해서 유의를 해야합니다. Predicate는 출력으로 전달되는 요소의 상태를 변경하기 위해 한 번만 만족되어야하기 때문에 유의하지 않으면 기대하지 않는 스트림의 요소를 얻을 수 있습니다.

// filter()
List<Integer> numbers = List.of(1, 3, 7, 8, 15, 4)
        .stream()
        .filter(i -> i < 10)
        .collect(toList());

// numbers = 1, 3, 7, 8, 4


// takeWhile() : default Stream<T> takeWhile(Predicate<? super T> predicate)
List<Integer> numbers = List.of(1, 3, 7, 8, 15, 4)
        .stream()
        .takeWhile(i -> i < 10)
        .collect(toList());

// numbers = 1, 3, 7, 8
// dropWhile() : default Stream<T> dropWhile(Predicate<? super T> predicate)

List<Integer> numbers = List.of(1, 3, 7, 8, 15, 4)
        .stream()
        .dropWhile(i -> i < 10)
        .collect(toList());

// numbers = [15, 4]

세 번째 새로운 메소드는 ofNullable (T t)입니다. 전달 된 값이 null인지 여부에 따라 Stream.emptry() 또는 Stream을 반환합니다. 이것은 스트림을 생성하기 전에 널 체크를 제거하는 것에 매우 유용 할 수 있으며, 이전 섹션에서 논의한 Optional 클래스의 새로운 stream () 메소드와 의미면에서 비슷합니다.

// getStudentByIdNo can return null
Student student = getStudentByIdNo(studentIdNo);

// Original
Optional.ofNullable(student)    // Optional(student)
    .map(student::streamGrades) // [optional(90), optional(60), optional(70)]
    .orElse(Stream.empty())     // null 체크
    .// do something with stream of Grades


// Java 9 Stream
Stream.ofNullable(student)          // Stream : [studnet]
    .flatMap(student::streamGrades) // [90, 60, 70]
    .// do something with stream of Grades

마지막으로 새로운 스트림 메소드는 정적 iterate () 메소드의 새 버전입니다. JDK 8의 버전은 하나의 매개 변수를 시드로 사용하여 무한의 스트림을 출력으로 생성했습니다. JDK 9에는 세 개의 매개 변수를 사용하는 오버로드 된 메서드가 추가되어 루프 구문의 표준을 스트림으로 복제 할 수 있습니다. 예를 들어, Stream.iterate (0, i -> i < 5, i -> i + 1)는 0에서 4까지의 정수 스트림을 제공합니다.

// 무한 스트림 생성
intStream.iterate(0, i -> i + 1) // limit? 이 필요
         .forEach(System.out::println);

// Java 9의 3개의 매개 변수를 사용한 메서드
intStream.iterate(0, i -> i < 5, i -> i+1)
         .forEach(System.out::println);

4. Read-Eval-Print Loop: jshell (JEP 222)

Python, Ruby, NodeJS 등의 언어들은 이미 제공하고 있는 REPL(Read-Eval-Print-Loop) 기능이 이번 JAVA9 에는 새로운 PEPL command-line tool인 jshell을 포함 합니다.

  • jshell은 컴파일, 수정, 실행을 IDE를 사용하지 않고 쉽게 자바 코드의 개발과 테스트를 할 수 있게 합니다.
  • 개발자는 지속적으로 입력 값 또는 입력 변경 상태, 결과에 대한 정보를 출력 할때, 코드 섹션을 신속하게 프로토 타이핑 할 수 있습니다.
  • 이 도구를 사용하기 쉽게하기 위해 편집 가능한 기록, 탭 완성, 세미콜론 자동 추가, 미리 구별 가능한 imports 및 definitions와 같은 기능이 포함되어 있습니다.
  • 선언과 함께 정의된 타입에 대한 변수를 선언할 수 있습니다.
    (but this is still not dynamic typing: once the type is set, it can’t be changed).

  • jshell을 통해 java 언어 교육이 더욱 쉬워지며 새로운 개발자가 결과를 얻기 전에 작성해야하는 코드의 양을 줄입니다.

=> 자세한 사항은 좌측의 JShell: Read-Evaluate-Print Loop for the Java Platform 세션 참고.

$ jshell
|  Welcome to JShell -- Version 9-ea
|  For an introduction type: /help intro

jshell> 3+3
$1 ==> 6

jshell> int x=5
x ==> 5

jshell> x
x ==> 5

jshell> void print() { System.out.println("Hello JShell"); }
|  created method print()

jshell> print()
Hello JShell

5. Concurrency Updates (JEP 266)

History

  • Java 5 : multiple cooperating threads
  • Java 7 : fork/join
  • Java 8 : parallel streams
  • Now, JDK 9 provides enhancements for concurrency in two areas

JDK 9 Concurrency updates

참고 :

첫 번째는 Reactive Stream Publish-Subscribe 프레임 워크입니다.

Reactive Streams

일반적으로 비동기 방식으로 스트림을 처리하면 요소가 처리 코드에 제출되는 속도가 급격히 높아질 때 문제가 발생할 수 있습니다. 프로세서가 Data를 처리 할 수 ​​없으면 요소 Publish가 차단되거나 버퍼 오버플로 상황이 발생할 수 있습니다.
-- the pain of back-pressure

Reactive Stream는 스트림 프로세서 (Subscriber)가 요소 공급 업체 (Publisher)를 구독하는 게시-구독 모델을 사용합니다. 이렇게함으로써, 공급자 언제든지 프로세서에 전달할 수 있는 요소의 수를 알고 있습니다. Publisher가 이 번호보다 많은 숫자를 보내야하는 경우 로컬 버퍼링과 같은 기술을 사용할 수도 있고 대체 프로세서를 사용할 수도 있습니다. (이는 증가된 로드를 처리하기 위해 새로운 서비스 인스턴스를 회전시켜 수평 확장 성을 제공하는 일반적인 마이크로 서비스 방식입니다)

Reactive Streams을 요약하면

  • 잠재적으로 무제한의 요소들을 처리합니다.
  • 순서대로 처리합니다.
  • 구성 요소간에 비동기적으로 전달합니다.
  • the pain of back-pressure 문제를 차단합니다.

Publish-Subscribe Pattern with Flow Control

다음과 같은 양뱡향 연결을 지원합니다.

  • Subscribe가 Publish에게 수요 요청에 대한 신호
  • Publish에서 Subscribe로 방출되는 데이터 흐름

subscriber 가 수요 요청을 내면 publisher는 안전하게 요청 수를 높일 수 있습니다. 이 동작을 통해 자원을 낭비를 막는 것에 도움이 됩니다. 수요가 비동기적으로 호출되기 때문에 publisher는 용량에 따라 향후 작업을 위해 최대한 많은 요청을 보낼 수 있습니다.

subscriber가 publisher 보다 느리다면, pull-based system 형태로 작동할 것이고 subscriber이 빠를 때는 push-based system 형태로 작동하게 됩니다.

JDK 9에는 Flow.Processor, Flow.Subscriber, Flow.Publisher 및 Flow.Subscription과 같은 여러 인터페이스를 포함하는 새로운 클래스인 Flow가 포함 되어 있습니다. subscription은 publisher를 subscriber와 연결하는 데 사용됩니다. subscription인터페이스에는 두 가지 메서드가 포함됩니다.

  • cancel () : 이 메서드는 subscriber가 메시지 수신을 중지하게 합니다.
  • request (long n) : publisher가 처리할 수 있는 숫자에 n개의 요소를 추가합니다. 이 메서드는 subscriber가 요소를 처리하고 더 많은 것을 처리할 준비가 되면 반복적으로 호출될 수 있습니다.

프로세서 인터페이스를 구현함으로써 클래스는 publisher와 subscriber의 중간 작업을 수행 할 수 있습니다.

JDK 9의 두번째 새로운 Concurrency 기능은 JDK 8에서 소개된 CompletableFuture 추가 기능 입니다.

참고 : http://www.esynergy-solutions.co.uk/blog/asynchronous-timeouts-completable-future-java-8-and-9

Future<Double> future = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(2000);
    } catch (Exception e){
        e.printStackTrace();
    }

    return 1000.0;
});

System.out.println("비동기 처리를 하는 동안 다른 일처리.");

try {
    // 타임아웃 3초로 지정.
    System.out.println("결과 : " + future.get(3000, TimeUnit.MILLISECONDS));
} catch (Exception e) {
    e.printStackTrace();
}

JDK 9에서는 CompletetableFutrue 클래스에 대해 시간 기반 기능이 추가되었습니다.

orTimeout()
: 내부적으로 ScheduledThreadExecutor를 사용하고 지정된 제한 시간이 경과 한 후, TimeoutException을 사용하여 CompletableFuture를 완료합니다. 또한 다른 새로운 CompletableFuture를 반환합니다. 아래의 코드에서 whenComplete가 타임 아웃이 발생하기 전에 발생할 수 있는 다른 예외적인 완료를 처리 할 수 있습니다.

/// public CompletableFuture<T> orTimeout(long timeout, TimeUnit unit)

// great british pound = GBP, LDN = 영국, NYV = 뉴욕
CompletableFuture.supplyAsync(() -> findBestPrice("LDN - NYC"), executorService)
    .orTimeout(1, TimeUnit.SECONDS)
    .whenComplete((amount, error) -> {
        if (error == null) {
            System.out.println("The price is: " + amount + "GBP");
        } else {
            System.out.println("Sorry, we could not return you a result");
        }
    }
);

completeOnTimeout()
: 또한 내부적으로 ScheduledThreadExecutor를 사용하지만 orTimeout과 달리 CompletableFuture 파이프 라인이 시간 초과가 될 경우 기본값을 제공합니다.

/// public CompletableFuture<T> completeOnTimeout(T value, long timeout, TimeUnit unit)

CompletableFuture.supplyAsync(() -> findBestPrice("LDN - NYC"), executorService)
    .completeOnTimeout(DEFAULT_PRICE, 1, TimeUnit.SECONDS)
    .thenAccept(amount -> {
        System.out.println("The price is: " + amount + "GBP");
    }
);

delayedExecutor()
: 주어진 시간 딜레이 이후에 기본 실행자로 보내진 새로운 default Executor를 반환.

/// static Executor delayedExecutor(long delay, TimeUnit unit);

CompletableFuture future = new CompletableFuture<>();

future.completeAsync(() -> "Inside future task", CompletableFuture.delayedExecutor(5, SECONDS))
    .thenAccept(result -> System.out.println("accept: " + result))
    .get();

// output
// accept: Inside future task

추가적으로 새로운 메서드가 몇 개 있습니다.

failedFuture()
newIncompleteFuture()

6. Milling Project Coin (JEP 213)

Milling Project Coin이란 반복적으로 수행하는 일반적인 작업 중 일부를 원활하게 해주는 작은 변화를 뜻한.다

try-with-resources

// Java 7 이전
SomeResource resource = null;
try {
    resource = getResource();
    use(resource);
} catch(...) {
    ...
} finally {
    if (resource != null) {
        try { resource.close(); } catch(...) { /* 아무것도 안 함 */ }
    }
}

// 공통 선언
SomeResource resource = getResource();

// Java 7 : try-with-resources
try (SomeResource resource2 = resource) {
    use(resource);
} catch(...) {
    ...
}

// Java 9 : try-wtih-resources
try (resource) {

} catch(...) {
    ...
}

Supporting private methods in interfaces

참고 : http://www.journaldev.com/12850/java9-private-methods-in-interface

Java 8에서는 default와 static methods를 선언할 수 있으나 private으로는 선언할 수 없습니다.

Java 9에서는 private method가 static 혹은 non-default instance method에 대해 private 키워드로 선언될 수 있습니다.

=> private methods는 non-private default, static methods에 공통적으로 사용할 수 있는 helper methods의 역할을 할 수 있습니다.

Java 7 Java 8 Java 9
Constant variables, Abstract methods Default, Static Methods Private, Private Static Methods
// Java 7 : Interfaces
public interface DBLogging{
    String MONGO_DB_NAME     = "ABC_Mongo_Datastore";
    String NEO4J_DB_NAME     = "ABC_Neo4J_Datastore";
    String CASSANDRA_DB_NAME = "ABC_Cassandra_Datastore";

    void logInfo(String message);
    void logWarn(String message);
    void logError(String message);
    void logFatal(String message);
}

// Java 8 : Interfaces
public interface DBLogging{
    String MONGO_DB_NAME     = "ABC_Mongo_Datastore";
    String NEO4J_DB_NAME     = "ABC_Neo4J_Datastore";
    String CASSANDRA_DB_NAME = "ABC_Cassandra_Datastore";

    default void logInfo(String message){
       //Step1: Connect to DataStore
       //Setp2: Log Info Message
       //Setp3: Close the DataStore connection
    }
    default void logWarn(String message){
       //Step1: Connect to DataStore
       //Setp2: Log Warn Message
       //Setp3: Close the DataStore connection
    }
    default void logError(String message){
       //Step1: Connect to DataStore
       //Setp2: Log Error Message
       //Setp3: Close the DataStore connection
    }
    default void logFatal(String message){
       //Step1: Connect to DataStore
       //Setp2: Log Fatal Message
       //Setp3: Close the DataStore connection  
    }
    // Any other abstract methods
}

// Java 9 : Interfaces
public interface DBLogging{
    String MONGO_DB_NAME     = "ABC_Mongo_Datastore";
    String NEO4J_DB_NAME     = "ABC_Neo4J_Datastore";
    String CASSANDRA_DB_NAME = "ABC_Cassandra_Datastore";

    default void logInfo(String message) { log(message, "INFO") }
    default void logWarn(String message) { log(message, "WARN") }
    default void logError(String message){ log(message, "ERROR") }
    default void logFatal(String message){ log(message, "FATAL") }

    private void log(String message, String msgPrefix){
       //Step1: Connect to DataStore
       //Setp2: Log Message with Prefix and styles etc.
       //Setp3: Close the DataStore connection  
    }
    // Any other abstract methods
}

Diamond Operator for Anonymous Inner Class

참고 : http://www.journaldev.com/12850/java9-private-methods-in-interface

Diamond Operator

List<Integer> list = new List();          // 현재는 에러남 예전 방식
List<Integer> list = new List<Integer>(); // 일반적인 명시 방식
List<Integer> list = new List<>();        // Diamond Operator

Anonymous Inner Class

public abstract class MyHandler<T> {

    // constructor, getter, setter...

    abstract void handle();
}

...

MyHandler<Integer> intHandler = new MyHandler<Integer>(1) {
    public void handle() {
        // handling code...
    }
};


// In Java 8 => Compile error
MyHandler<Integer> intHandler = new MyHandler<>(10) { 
    // Anonymous Class 
};

// <?> : 모든 객체 자료형, 내부적으로는 Object로 인식
MyHandler<?> handler = new MyHandler<>("One hundred") { 
    // Anonymous Class 
};

In Java 9 Diamond Operator for Anonymous Inner Class

// Integer
MyHandler<Integer> intHandler = new MyHandler<>(1) {
    @Override
    public void handle() {
        // handling code...
    }
};

// String
MyHandler<?> handler = new MyHandler<>("One hundred") {
    @Override
    void handle() {
        // handling code...
    }
};

“_” (Underscore) changes

Java에서 Underscore를 사용하는 주된 용도,

  • 식별자의 두 단어를 연결 (web_page)
  • 상수를 정의 (_pi)
  • private 변수, 메서드 등을 정의하는 것.

Java 8에서는 "_"를 단독으로 사용할 수 있습니다.

int _ = 10;

System.out.println("Value of underscore (_) = " + _);
// Multiple markers at this line
// - '_' should not be used as an identifier, since it is a reserved keyword from source level 1.8 on
// - Occurrence of '_'
// 이는 Underscore가 Java SE 8 이후의 식별자로 허용되지 않음을 의미합니다.

Java 9에서는 "_"은 식별자로 더 이상 유효하지 않습니다.

jshell> int _ = 10
|  Error:
|  as of release 9, '_' is a keyword, and may not be used as an identifier
|  int _ = 10
|      ^
|  Error:
|  reached end of file while parsing
|  int _ = 10
  • 하나의 "_"를 람다식에서만 변수 이름으로 사용하는 것 가능합니다.
  • 람다 표현식이 하나의 인수만 취하고 람다 본문에서 인수를 사용하지 않으면 단일 "_"을 사용할 수 있습니다.
  • "_" 개념을 좋아하는 사용자의 경우, 변수 이름에 사용하더라도 두 개 이상을 사용해야 식별자로 사용할 수 있습니다.
// jshell 테스트 결과 (java 9)

int _ = 10;   // failed

int a_b = 10; // ok

int __ = 10;  //ok

_ -> getX ()  // ok

@SafeVarargs의 확장: Generic 타입에 가변인자를 사용한다.

constructors, static methods, final methods 등 재정의할 수 없는 요소에서만 가능합니다.
private methods의 경우, subclass에 의해 오버라이딩 될 수 없기 때문에 @SafeVarargs를 사용할 수 있습니다.

7. Spin-Wait Hints (JEP 285)

참고 : https://www.sitepoint.com/ultimate-guide-to-java-9/#spinwaithints

  • 이 기능의 흥미로운 점은 오라클 (이 경우 Azul Systems)이 아닌 다른 회사에서 제안한 첫 번째 JEP라는 것입니다.
  • onSpinWait () 메서드는 스레드가 현재 프로세서 spin loop에 있음을 JVM에 알리기 위한 것입니다.
  • JVM 및 하드웨어 플랫폼이 회전 할 때 최적화를 지원하면 이러한 힌트를 사용할 수 있습니다. 그렇지 않으면 호출이 무시됩니다.
  • 일반적인 최적화에는 스레드 간 대기 시간 감소 및 전력 소비 감소가 포함됩니다.
class EventHandler {
    volatile boolean eventNotificationReceived;

    void waitForEventAndHandleIt() {
        while (!eventNotificationReceived) { /* spinning */ }
        readAndProcessEvent();
    }

    void readAndProcessEvent() { /* ... */ }
}
void waitForEventAndHandleIt() {
    while (!eventNotificationReceived) {
        Thread.onSpinWait();
    }
    readAndProcessEvent();
}

8. Variable Handles (JEP 193)

변수는 Java에서 항상 사용됩니다. 변수는 값을 보유하는 메모리 영역에 대한 암시적 포인터입니다.

Variable Handle은 변수에 대한 typed reference 입니다.
(C 및 C ++를 사용하는 사람들은 포인터에 대한 포인터입니다)

  • VarHandles는 MethodHandles API를 사용하여 연관된 클래스 내의 필드를 조회하여 작성됩니다. (VarHandle에 대한 참조를 얻으려면 JDK 9에서 확장된 MethodHandle.Lookup 클래스를 사용합니다.)
  • 필요에 따라 정적 변수 또는 비 정적 변수에 대한 참조는 물론 배열로 검색 할 수 있습니다.
  • VarHandle을 가지고 있으면 참조하는 변수에 대해 저수준 메모리 순서 지정 작업을 실행할 수 있습니다. 또한 VarHandle을 사용하여 "fence" 작업을 수행 할 수 있으므로 읽기 및 쓰기, 저장 및 로드와 같은 작업의 메모리 순서를 제어 할 수 있습니다.
  • VarHandle API는 Sun.misc.Unsafe와 동등한 성능 결과를 제공하고 Atomic 클래스보다 성능이 우수한 결과를 제공합니다.

For Example

// General Class
public class Record {
    private long version = 0;

    public long update() {
        return ++version;
    }
}

// Basic Synchronized Class
public class Record {
    private volatile long version = 0;

    public synchronized  long update() {
        return ++version;
    }
}

// Atomic Class
public class Record {
    private final AtomicLong version = new AtomicLong(0);

    public long update() {
        return version.incrementAndGet();
    }
}

// AtomicXFieldUpdater classes
public class Record {
    private static final AtomicLongFieldUpdater<Record> VERSION =
        AtomicLongFieldUpdater.newUpdater(Record.class, "version");

    private volatile long version = 0;

    public long update() {
        return VERSION.incrementAndGet(this);
    }
}

// VarHandle API
public class Record {
    private static final VarHandle VERSION;

    static {
        try {
            VERSION = MethodHandles.lookup().findFieldVarHandle
                 (Record.class, "version", long.class);
        } catch (Exception e) {
            throw new Error(e);
        }
    }

    private volatile long version = 0;

    public long update() {
         return (long) VERSION.addAndGet(this, 1);
    }
}

9. Process API Updates (JEP 102)

참조 : http://iteratrlearning.com/java/2017/03/12/java9-process-api.html

JDK 9에는 Process 및 ProcessBuilder 클래스의 향상된 기능이 포함되어 있으며 새로운 ProcessHandle 인터페이스가 도입되었습니다. Process API를 통해 프로덕션 응용 프로그램의 모니터링, 관리 및 배포와 관련된 툴을 작성할 수 있습니다.

이제 ProcessBuilder에 startAsPipeline () 메서드가 포함됩니다. 이름에서 알 수 있듯이 이 메서드는 ProcessBuilders 목록을 사용하여 프로세스의 UNIX 스타일 파이프 라인을 만듭니다. 각 프로세스가 시작되면 표준 출력이 다음 프로세스의 표준 입력에 연결됩니다.

// ls /tmp | wc -l
// tmp 디렉토리에 있는 파일 수를 구한다.

ProcessBuilder ls = new ProcessBuilder().command("ls").directory(Paths.get("/tmp").toFile()); 
ProcessBuilder wc = new ProcessBuilder().command("wc", "-l").redirectOutput(Redirect.INHERIT); 

List lsPipeWc = ProcessBuilder.startPipeline(asList(ls, wc));

ProcessHandle 인터페이스는 고유 프로세스를 식별하고 제어합니다. 프로세스 ID뿐만 아니라 하위 및 하위 프로세스와 같은 프로세스에 대한 정보를 검색하는 메소드를 제공합니다. 또한 주어진 프로세스 ID에 대해 Optional을 반환하는 정적 메서드가 있습니다.

ProcessHandle.Info 하위 인터페이스는 프로세스를 시작하는 데 사용 된 명령 줄, 프로세스가 시작된시기 및 프로세스를 시작한 사용자의 ID와 같은 프로세스 세부 정보의 범위를 제공합니다. Process 클래스에는 네이티브 프로세스에 대한 추가 정보에 대한 액세스를 제공하는 7개의 새 메서드가 있습니다.

  • children() and descendants(): 프로세스의 하위 또는 종속 프로그램을 각각 나열합니다.
  • getPid(): 지정된 프로세스의 ID를 반환합니다.
  • info(): Process Handle.Info 인스턴스로 정보의 스냅 샷을 반환합니다.
  • onExit(): 종료가 발생할 때 작업을 수행하는 데 사용할 수있는 CompletableFuture 입니다.
  • supportsNormalTermination(): destroy ()가 프로세스를 정상적으로 종료하는지 결정합니다.
  • toHandle(): 이 프로세스의 ProcessHandle를 반환합니다.
// 특정 어플리케이션 모두 종료 함수 만들기

// application 확인 함수
boolean isApplication(final String appName, final ProcessHandle process)
{
   return process.info().command().filter(command ->
     command.contains(appName)).isPresent();
}

// 로그 출력 후 프로세스 종료
void closeAndLog(ProcessHandle process, String command)
{
   String status = process.destroyForcibly() ? " Success!" : " Failed";
   System.out.println("Killing ... " + command + status);
}

// 특정 어플리케이션 모두 종료
void cleanupApplication(String applicationName)
{
  ProcessHandle
    .allProcesses()
    .filter(process -> isApplication(applicationName, process))
    .forEach(process ->
      process.info().command().ifPresent(command ->
        closeAndLog(process, command)));

}

results matching ""

    No results matching ""