[아이템 48] 스트림 병렬화는 주의해서 적용하라.

자바 8에 추가된 parallel 메서드는 파이프라인을 병렬 실행할 수 있습니다. 동시성 프로그래밍을 할 때는 안전성과 응답 가능 상태를 유지하기 위해 주의를 기울여야합니다. 데이터 소스가 Stream.iterate거나 중간 연산으로 limit를 쓰면 파이프라인 병렬화로 성능 개선을 기대할 수 없습니다.

대체로 스트림 소스가 ArrayList, HashMap, HashSet, ConcurrentHashMap의 인스턴스거나 배열, int 범위, long 범위일 때 병렬화의 효과가 가장 좋습니다. 이 자료구조들은 데이터를 원하는 크기로 정확하고 손쉽게 나눌 수 있어서 다수의 스레드에 분배하기 좋다는 특성이 있습니다. 나누는 작업은 Spliterator가 담당하고 Spliterator 객체는 Stream이나 Iterable의 spliterator 메서드로 얻어올 수 있습니다. 또한 원소들을 순차적으로 실행할 때 참조 지역성(locality of reference) 가 뛰어납니다.


참조 지역성이란 이웃한 원소의 참조들이 메모르에 연속해서 저장되어 있다는 뜻입니다. 만일 참조들이 가리키는 실제 객체가 메모르에 서로 떨어져 있을 경우 참조 지역성이 나빠집니다. 참조 지역성이 낮으면 스레드는 데이터가 주 메모르에서 캐시 메모리로 전송되어 오기를 기다리며 시간을 보내게 됩니다.

참조 지역성이 가장 뛰어난 자료구조는 기본 타입의 배열인데 이는 데이터 자체가 메모리에 연속해서 저장되기 때문입니다.

스트림 파이프라인의 최종 연산의 동작 역시 병렬 수행 효율에 영향을 줍니다. 최종 연산 중 병렬화에 가장 적합한 것은 축소입니다. Stream의 reduce 메서드 중 하나 혹은 min, max, sum, count 같이 완성된 형태로 제공되는 메서드를 선택해 수행합니다. anyMatch, allMatch, noneMath처럼 조건에 맞으면 바로 반환되는 메서드도 병렬화에 적합합니다. 반면 가변 축소를 수행하는 Stream의 collect 메서드는 병렬화에 적합하지 안흔데 이는 컬렉션들을 합치는 부담이 크기 때문입니다.



스트림을 잘못 정렬화하면 결과 자체가 달라지거나 예상치 못한 동작이 발생할 수 있습니다. 이를 안전 실패라 부릅니다. 안전 실패는 스트림 파이프라인에서 사용하는 mapper, filters 혹은 프로그래머가 제공한 다른 함수 객체가 명세대로 동작하지 않을 때 벌어질 수 있습니다.