Post

2번 읽는 Modern Java In Action - Chapter01 기초

개요

이 책을 사게 된 계기는 원래 읽던 책인 effective Java를 읽다가 내용이 머릿속에 잘 안들어왔고, 자바의 대한 이해가 좀 더 필요하다고 느껴서 주변에 물어봐서 effective java 보다 잘 읽히는 책인 Modern Java In Action을 구매하게 되었다.

1장이라 코드보다는 설명이 많다.

Java8에서는 Stream API를 통해서 멀티코어 환경에서 좀 더 안정적, 간결하게 짤 수 있음을 말한다. 그 이유는 메서드에 코드를 전달하는 람다, 메서드 참조를 통해 불필요한 로직 중복을 줄일 수 있으며, 스트림 라이브러리가 최적의 저수준 실행방법을 선택하여 멀티코어 환경에서도 비용이 비싼 synchronized를 사용하지 않게 할 수 있다.

즉, 앞으로 Java8에서 제공하는 세 가지 프로그래밍 개념을 통해 멀티코어 병렬성이 강화된 내용을 설명한다.

📜 스트림 처리

스트림은 한 번에 한 개씩 만들어지는 연속적인 데이터 항목들의 모임이다.

프로그램은 입력 스트림에서 한 개씩 데이터를 읽어 들이며 마찬가지로 한 개씩 출력 스트림으로 데이터를 기록한다.

1
cat file1 file2 | tr "[A-Z]" "[a-z]" | sort | tail -3
이라는 파이프()로 구성한 유닉스 명령어가 있다.

이는 일반적으로 병렬적으로 실행된다. 즉, cat명령어가 끝나지 않아도, sort를 처리할 수도 있다.

어떤식으로 병렬적으로 해결할까? 사실 파이프는 앞에서 처리한 데이터들을 input으로 받아서 처리해야한다. 이게 어떻게 병렬적이란 것인가?에 대해 생각해봤다.

이는 각 명령어를 수행하는 자식 프로세스가 생기며(병렬성) 결국 앞에서 수행한 명령어를 input으로 받아서 작업을 처리하며 넘긴다.

참고: https://hwan-shell.tistory.com/324

Java8에는 위와 비슷한 개념의 Stream API를 통해 스레드라는 복잡한 작업을 사용하지 않으면서 병렬성을 얻을 수 있게 되었다.

개인 의견: 추가적으로 항상 병렬 스트림을 이용하였다고 하여, 항상 성능을 높일 수 있는건 아니다. 기본적으로 병렬스트림은 스레드를 여러개 생성해서 동작하기때문에 그 비용이 만만치 않기 때문이다. 즉, 대용량 데이터에서 병렬 스트림을 적용하는것을 고려할만하다.

📜 동작 파라미터화로 메서드에 코드 전달

만약 어떤 나라에서 다른 나라로 돈을 보낼때 ID값을 이런식으로 구성한다고 하자

1
2
2023CADKRW12345
연도/전송할 나라/받은 나라/id

위의 유닉스 예제에서 sort에 연도별로 정렬해야할 때도 있고, 전송할 나라로 정렬해야할 때도 있고, 받은 나라로 정렬해야할 때가 있다고 하면 sort명령어에 인자를 넘겨줘야한다.

Java8 전에는 Comparator객체를 만들어서 커스텀된 정렬 메서드를 넘겨주는 방법이 있지만, 코드가 복잡하고 결국 구현해야할 각각의 메서드들도 로직적으로 공통사항이 생겨 중복 코드가 생길 수 밖에 없다.

Java8부터는 메서드를 다른 메서드의 인수로 넘겨주는 기능을 제공한다고 한다. 궁금하지만 곧 2장에서 나온다니, 일단 넘어가고 내가 따로 학습해야할 내용은 그때 추가하겠다.

📜 병렬성과 공유 가변 데이터

세 번째는 스트림을 통해 병렬성을 얻는 대신에 항상 안전하게 코드를 실행할 수 있도록 공유된 가변 데이터에 접근하지 않아야 한다.

즉, 공유된 변수나 객체가 있으면 병렬성에 문제가 생길 수 있다. 병렬성을 통해 명령을 수행하는 여러 프로세스가 생겨날텐데, 여러 프로세스에서 공유된 변수를 동시에 바꾸려하면 어떻게 될까?

먼저 synchronized 키워드를 통해 해결할 수는 있겠다. 하지만 이러면 병렬성의 이점이 사라진다. 결국 순차적으로 코드가 실행되어야하고 대기하는 프로세스도 많아져서 성능상의 이슈가 있을 수 있다.

개인 의견: 기본적으로 자바에서 제공하는 ConcurrentHashMap같이 안전한 자료구조를 제공하는 방법이 생각난다. 책에서 향후 또 다룰 것 같다.

📜 메서드 참조

메서드 참조란 개념은 자바8에서 새로 생긴 기능이다. 기존에는 어떠한 필터작업을 할 때 객체를 생성해서 메서드에 넘기는 방식으로 작업을 하였다.

1
2
3
4
5
6
7
8
9
10
@Test
void 자바8이전작업처리(){
    File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
        @Override
        public boolean accept(File pathname) {
            return pathname.isHidden();
        }
    });

}

숨김파일을 찾는 코드인데, 코드 수도 너무 길고 굳이 FileFilter를 인스턴스화해서 해결한다.

자바8 이후에는 간단하게 코드를 짤 수 있다.

1
2
3
4
    @Test
    void 자바8이후작업처리(){
        File[] Java8HiddenFiles = new File(".").listFiles(File::isHidden);
    }

::라는 메서드 참조를 이용하여 간단하게 작성이 가능하다. 내부동작은 3장에 나온다하니 일단은 넘어가기로 했다.

메서드 참조는 객체를 생성하지 않고 런타임 시에 메서드를 참조하여 호출할 수 있는 기능이다.

  • 이후 책에는 람다와 스트림 자바8에 추가된 기능에 대한 간단한 예제에 대한 내용이 나오지만, 어차피 점차 자세히 다뤄지고 있으므로 시간이 아까워서 여기서 정리하지 않을 것이다.
This post is licensed under CC BY 4.0 by the author.