1.1 역사의 흐름은 무엇인가?
자바 8에서는 대대적인 변화가 있었다.
멀티코어 CPU 대중화 같은 하드웨어 변화가 자바 8에 영향을 미쳤다.
자바 8이 등장하기 전에는 코어를 활요하려면 스레드를 사용하는것이 좋았지만,
스레드는 관리하기 어렵고 많은 문제가 생길 수 있다.
자바는 이런 병렬 실행 환결을 쉽게 관리하고 에러가 덜 발생하는 뱡향으로 변경되려 노력했다.
자바1.0 에서는 스레드 와 락, 메모리 모델까지 지원했었는데 이런 저수준 기능들을 활용하긴 어려웠다.
자바 5에서는 스레드 풀, 병렬 실행 컬렉션 등 강력한 도구들이 나왔고
자바 7에서는 병렬에 도움을 줄 수 있는 포크, 조인 등이 나왔지만 여전히 활용하기 어려웠다.
자바 8에서 단순한 방식으로 병렬실행을 할 수 있는 방법을 제공했다.
자바 9에서는 리엑티브 프로그래밍 병렬 실행 기법을 지원한다. 이걸 사용하는 상황은 한정 되어있지만 고성능 병렬 시스템에서 인기를 얻고있는 RxJava 를 표준적인 방식으로 지원한다.
다시 자바 8로 와서,
자바 8은 간결한 코드, 멀티코어 프로세서의 쉬운 활용이라는 두 요구사항을 기반으로 나왔다.
자바 8에서 제공한 새로운 기술은
- 스트림 API
- 메서드에 코드를 전달하는 기법
- 인터페이스의 디폴트 메소드
가 있다.
병렬 연산을 지원하는 스트림 API로 에러를 자주 일으키는 synchonized 키워드를 사용하지 않아도 되었다.
메서드에 코드를 전달하는 자바8 기법은 함수형 프로그래밍에서 위력을 발휘한다.
1.2 왜 아직도 자바는 변화하는가?
프로그래밍 언어는 생태계와 닮아서 새로운 언어가 등장하면 기존 언어는 사장되기도 한다.
프로그래밍 언어 생태계에서 자바의 위치
자바는 처음부터 많은 라이브러리를 포함한 잘 설계된 객체지향 언어로 시작됐다.
처음부터 스레드와 락 을 이용한 동시성도 지원했었다.
코드를 JVM 바이트 코드로 컴파일 하는 특징 때문에 인터넷 애플릿 프로그램의 주요 언어가 되었다.
자바 8은 다양한 프로그래밍 문제를 더 빠르고 정확하며 쉽게 유지보수 할 수 있다는 장점을 가진다.
자바 8의 모태인 세가지 프로그래밍 개념을 자세히 보고,
자바 8의 새로운 멀티코어 병렬성이 강화된 이유를 보자
스트림 처리
스트림은 입력 스트림에서 데이터를 한개씩 읽어서 출력 스트림으로 데이터를 한개씩 기록한다.
조립 라인처럼 어떤 항목을 연속으로 제공하는 어떤 기능이라고 생각하자.
스트림의 핵심은 기존에는 한번에 한 항목을 처리했지만
작업을 고수준으로 추상화해서 일련의 스트림으로 만들어 처리 할 수 있다는 것과
스레드라는 복잡한 작업을 사용하지 않으면서도 병렬성을 얻을 수 있다는 것
동작 파라미터화로 메서드에 코드 전달하기
자바 8에서 코드 일부를 API 로 전달하는 기능을 뜻한다.
자바 8에서는 메서드를 다른 메서드의 인수로 넘겨주는 기능을 제공한다.
이런 기능을 이론적으로 동작 파라미터화라고 부른다.
병렬성과 공유 가변 데이터
병렬성을 공짜로 얻을 수 있는 부분
스트림 메서드에 전달하는 코드는 다른 코드와 동시에 실행하더라도 안전하게 실행되어야 한다.
안전하게 실행하려면 공유된 가변 데이터 에 접근 하지 않아야 한다.
이런 공유 가변 데이터가 없는 함수를 순수 함수, 상태 없는 함수라 부르기도 한다.
공유된 변수를 동시에 변경하려면 synchronized 를 사용하는 방법등이 있을 수 있지만
생각보다 비싼 대가를 치러야 한다.
공유되지 않은 가변 데이터, 메소드, 함수를 다른 메소드에 전달하는 기능은 함수형 프로그래밍의 핵심사항이라 할 수 있다.
이제 자바 8에 추가된 새로운 개념을 하나씩 자세히 봐보자
1.3 자바 함수
프로그래밍 언어의 핵심은 값을 바꾸는 것
전통적으로 프로그래밍 언어에서는 값을 바꿀수있는 값을 일급(퍼스트 클래스) 값이라고 불렀다.
전달 할 수 없는 구조체(메소드, 클래스 등)는 이급시민 이라고 했는데,
자바 8에서는 이 이급 시민을 일급 으로 바꿀 수 있는 기능을 추가했다.
메서드와 람다를 일급 시민으로
메소드 참조라는 새로운 자바 8의 기능이 생겼다.
자바 8의 메소드 참조(:: (이 메소드를 값으로 사용하라)) 를 이용해서 직접 전달 할 수 있다.
ex) File::isHidden
자바 8에서는 더이상 메소드가 이급 값이 아니게 된다.
람다 :익명 함수
람다(익명 함수) 도 값으로 취급 할 수 있다.
(int x) -> x + 1
직접 메소드를 만들 수도 있지만 메소드가 없을 때 람다를 이용하면 더 간결하게 코드를 만들 수 있어진다.
메서드 전달에서 람다로
한두번만 사용하는 메서드를 매번 정의하는 것은 귀찮은 일이다.
람다를 이용해서 코드를 구현 할 수 있다.
넘겨주려는 코드를 찾을 필요도 없어지고 더 짧고 간결해진다.
1.4 스트림
스트림을 활용하면 컬렉션 API와는 상당히 다른 방식으로 데이터를 처리 할 수 있다.
또 컬렉션으로만 처리를 할때 거대한 리스트가 있다면 단일 CPU 만으로는 처리가 힘든데
서로 다른 CPU 코어를 이용해서 처리 시간을 줄일 수 있다면 좋을 것이다.
멀티스레딩은 어렵다.
멀티스레딩 코드를 이용해서 병렬성을 이용하는것은 쉽지 않다.
각 스레드가 동시에 공유 데이터에 접근하고 갱신 할 수 있다.
자바 8 스트림은 '컬렉션을 처리할때 발생하는 반복적인 코드들이 생기는 문제' 와
'멀티 코어 활용 어려움' 이 두가지 문제를 모두 해결했는데
반복적인 코드들은 컬렉션을 사용할때 자주 사용되는 패턴인,
조건에 따라 필터링하는 부분과, 데이터를 추출 하거나 그룹화 하는 등의 기능들이 있다.
또 이런 동작들을 쉽게 병렬화 할 수 있다는 점도 변화의 동기가 되었다.
병렬성 부분은
두개의 CPU 코어가 있을때 한 CPU 가 리스트의 앞부분을 처리하고 다른 CPU가 뒷부분을 처리할 때
이 과정을 포킹 단계라고 하고
이 과정 후에 CPU가 각자 리스트를 처리하고 마지막으로 결과를 정리한다.
스트림은 스트림 내의 요소를 쉽게 병렬로 처리 할 수 있는 환경을 제공해줘서
병렬 처리를 쉽게 할 수 있다.
(parallelStream()) 으로 병렬 처리 한다.
1.5 디폴트 메소드와 자바 모듈
자바 8 이전에는
외부에서 만들어진 컴포넌트를 이용해서 시스템을 구축할때
자바 패키지 집합을 포함하는 JAR 파일을 제공하는 것이 전부였다.
게다가 패키지의 인터페이스가 변경되면 모든 클래스를 변경 시켜야하는 문제점이 있엇다.
우선 자바9에서는 패키지 모음을 포함하는 모듈을 정의해서 JAR 같은 컴포넌트에 구조를 적용할 수 있었고
자바 8에서는 인터페이스를 쉽게 변경 가능하도록 디폴트 메소드를 제공해줬다.
인터페이스에 메소드를 추가하면 해당 인터페이스를 사용하는 모든 클래스에서
해당 메소드를 구현해줘야하는데 디폴트 메소드를 사용하면 구현하지 않아도 된다.
'Java > 모던 자바' 카테고리의 다른 글
동작 파라미터화 코드 전달하기 (0) | 2021.11.02 |
---|