Chapter 1. 무슨 일이 일어나고 있는가?
07 Feb 2022 | 모던 자바 인 액션 JAVA2. 왜 아직도 자바는 변화하는가?
스트림 처리
- 스트림: 한 번에 한 개씩 만들어지는 연속적인 데이터 항목들의 모임
- 프로그램은 입력 스트림에서 데이터를 한 개씩 읽어 들이며, 출력 스트림으로 데이터를 한 개씩 기록
- 어떤 프로그램의 출력 스트림은 다른 프로그램의 입력 스트림이 될 수 있음
- 자바 8에는
java.util.stream
패키지에 스트림 API가 추가됨- 스트림 패키지에 정의된
Stream<T>
는 T 형식으로 구셩된 일련의 항목을 의미 - 스트림 API는 파이프라인을 만드는 데 필요한 많은 메서드를 제공
- 스트림 패키지에 정의된
- 스트림 API의 핵심은 수행하려는 작업을 고수준으로 추상화해 일련의 스트림으로 만들어 처리할 수 있다는 것 + 스트림 파이프라인을 이용해 입력 부분을 여러 CPU 코어에 쉽게 할당할 수 있다는 것
- 스레드라는 복잡한 작업을 사용하지 않으면서도 공짜로 병렬성을 얻을 수 있음
동작 파라미터화로 메서드에 코드 전달하기
- 동작 파라미터화: 메서드를 다른 메서드의 인수로 넘겨주는 기능
- 자세한 내용은 2장과 3장 참고
병렬성과 공유 가변 데이터
- 스트림 메서드로 전달하는 코드는 다른 코드와 동시에 실행하더라도 안전하게 실행될 수 있어야 함
- 다른 코드와 동시에 실행하더라도 안전하게 실행할 수 있는 코드를 만들려면 공유된 가변 데이터에 접근하지 않아야 함
- 이러한 함수를 순수함수, 부작용 없는 함수, 상태 없는 함수라 부름 (18, 19장에서 설명)
- 공유된 변수나 객체가 있으면 병렬성에 문제가 발생
- 기존에는
synchronized
를 이용해 공유된 가변 데이터를 보호하는 규칙을 만듦 (일반적으로synchronized
는 시스템 성능에 악영향을 미침) - 자바 8을 이용하면 자바 스레드 API보다 쉽게 병렬성 활용이 가능
- 기존에는
- 공유되지 않은 가변 데이터, 메서드, 함수 코드를 다른 메서드로 전달하는 두 가지 기능은 함수형 프로그래밍 패러다임의 핵심적인 사항 (18, 19장에서 설명)
- 명령형 프로그래밍 패러다임은 일련의 가변 상태로 프로그램을 정의
- 공유되지 않은 가변 데이터 요구사항이란 인수를 결과로 변환하는 기능과 관련됨
- 요구사항은 수학적인 함수처람 함수가 정해진 기능만 수행하며 다른 부작용은 일으키지 않음을 의미
3. 자바 함수
- 프로그래밍 언어에서 함수(function)이라는 용어는 메서드(method) 특히 정적 메서드(static method)와 같은 의미로 사용됨
- 자바의 함수는 이에 더해 수학적인 함수처럼 사용되며 부작용을 일으키지 않는 함수를 의미
- 함수를 새로운 값의 형식으로 추가함
- 이유: 멀티코어에서 병렬 프로그래밍을 활용할 수 있는 스트림과 연계될 수 있도록 함수를 만들었기 때문
메서드와 람다를 일급 시민으로
메서드 참조
- ‘::‘(이 메서드를 값으로 사용하라는 의미)를 기호로 사용
- 메서드 참조 개념은 3장에서 설명
람다 : 익명 함수
- 람다를 포함하여 함수도 값으로 취급할 수 있음
- 람다 개념은 3장에서 설명
- 람다 문법 형식으로 구현된 프로그램을 함수형 프로그래밍 (함수를 일급값으로 넘겨주는 프로그램을 구현한다)라고 함
4. 스트림
- 컬렉션에서는 반복 과정을 직접 처리해야 함
- 외부 반복(external iteration): for-each 루프를 이용해 각 요소를 반복하면서 작업을 수행
- 스트림 API를 이용하면 루프를 사용하지 않고, 라이브러리 내부에서 모든 데이터가 처리 됨
- 내부 반복(internal iteration): 라이브러리 내부에서 모든 데이터가 처리됨 (4장에서 설명)
멀티스레딩은 어렵다
- 멅티스레딩 환경에서 각각의 스레드는 동시에 공유된 데이터에 접근하고, 데이터를 갱신할 수 있음
- 결과적으로 스레드를 잘 제어하지 못하면 원치 않는 방식으로 데이터가 바뀔 수 있음
- 멀티스레딩 모델은 순차적인 모델보다 다루기 어려움
-
자바 8 스트림 API(
java.util.stream
)로 ‘컬렉션을 처리하면서 발생하는 모호함과 반복적인 크도 문제’, ‘멀티코어 활용 어려움’이라는 두 가지 문제를 모두 해결 - 변화 동기 1. 기존의 컬렉션에서는 데이터를 처리할 때 반복되는 패턴이 너무 많았음. 따라서 라이브러리에서 반복되는 패턴을 제공한다면 좋을 것이라는 이이디어가 변화의 동기가 됨
- 자주 반복되는 패턴으로 주어진 조건에 따라 데이터를 필터링(filtering)하거나, 데이터를 추출(extracting)하거나, 데이터를 그룹화(grouping)하는 등의 기능이 있음
- 변화 동기 2. 이런 동작들을 쉽게 병렬화할 수 있다는 점도 변화의 동기가 됨
- 포킹 단계(forking step): 두 CPU를 가진 환경에서 리스트를 필터링할 대 한 CPU는 리스트의 앞부분을 처리하고, 다른 CPU는 리스트의 뒷부분을 처리하도록 요청할 수 있음
- 각각의 cPU는 자신이 맏은 절반의 리스트를 처리한 후, 마지막으로 하나의 CPU가 두 결과를 정리
- 컬렉션은 어떻게 데이터를 저장하고 접근할지에 중점을 두는 반면 스트림은 데이터에 어떤 계산을 할 것인지 묘사하는 것에 중점을 둠
- 스트림은 스트림 내의 요소를 쉽게 병렬로 처리할 수 있는 환경을 제공한다는 것이 핵심
- 필터링할 수 있는 가장 빠른 방법은 컬렉션을 스트림으로 바꾸고, 병렬로 처리한 다음, 리스트로 다시 복원하는 것
자바의 병렬성과 공유되지 않은 가변 상태
라이브러리를 분할 처리
큰 스트림을 병렬로 처리할 수 있도록 작은 스트림으로 분할
filter 같은 라이브러리 메서드로 전달된 메서드가 상호작용하지 않는다면 가변 공유 객체를 통해 병렬성을 누릴 수 있음
함수형 프로그래밍에서 함수형이란 ‘함수를 일급값으로 사용한다’라는 의미도 있지만, 부가적으로 ‘프로그램이 실행되는 동안 컴포넌트 간에 상호작용이 일어나지 않는다’라는 의미도 포함됨
5. 디폴트 메서드와 자바 모듈
- 자바 9의 모듈 시스템은 모듈을 정의하는 문법을 제공하므로 이를 이용해 패키지 모음을 포함하는 모듈을 정의할 수 있음
- 모듈 덕분에 JAR 같은 컴포넌트에 구조를 적용할 수 있고, 문서화와 모듈 확인 작업이 쉬워짐 (14장에서 설명)
- 자바 8에서는 인터페이스를 쉽게 바꿀 수 있도록 디폴트 메서드를 지원 (13장에서 설명)
- 디폴트 메서드는 특정 프로그램을 구현하는 데 도움을 주는 기능이 아니라 미래에 프로그램이 쉽게 변화할 수 있는 환경을 제공하는 기능
- 자바 8은 구현 클래스에서 구현하지 않아도 되는 메서드를 인터페이스에 추가할 수 있는 기능인 디폴트 메서드를 제공
- 메서드 본문은 클래스 구현이 아니라 인터페이스의 일부로 포함됨
- 디폴트 메서드를 이용하면 기존의 코드를 건드리지 않고 원래의 인터페이스 설계를 자유롭게 확장할 수 있음
- 자바 8에서는 인터페이스 규격명세에
default
라는 새로운 키워드를 지원
6. 함수형 프로그래밍에서 가져온 유용한 아이디어
- 자바에 포함된 함수형 프로그래밍의 핵심적인 두 아이디어
- 메서드와 람다를 일금값으로 사용하는 것
- 가변 공유 상태가 없는 병렬 실행을 이용해 효율적이고 안전하게 함수나 메서드를 호출할 수 있다는 것
- 자바 8에서는 NullPointer 예외를 피할 수 있도록 도와주는
Optional<T>
클래스를 제공 (11장에서 설명)Optional<T>
는 값을 갖거나 갖지 않을 수 있는 컨테이너 객체. 값이 없는 상황을 어떻게 처리할지 명시적으로 구현하는 메서드를 포함- 형식 시스템을 이용해 어떤 변수에 값이 없을 때 어떻게 처리할지 명시할 수 있음
Chapter 1 끝!!!