- 자바는 동시성 프로그래밍을 항상 지원하려고 노력했습니다.
- 처음부터 스레드, 동기화, wait/notify를 지원하였습니다.
- 자바5부터는 동시성 컬렉션인 java.util.concurrent 라이브러리와 실행자(Executor) 프레임워크를 지원하였으며, 자바 7부터는 고성능 병렬 분해 프레임워크인 포크-조인 패키지를 추가했습니다.
- 자바8부터는 스트림을 통해 병렬 실행할 수 있도록 지원하였습니다.
- 이 처럼 자바는 동시성 프로그래밍을 꾸준히 지원하는데, 동시성 프로그래밍을 할 때는 안정성과 응답 가능 상태를 유지를 해야 한다.
- 데이터 소스가 Stream.iterate거나 중간 연산으로 limit를 쓰면 파이프라인 병렬화로는 성능 개선을 기대할 수 없다.
- limit을 다룰 때 CPU 코어가 남는다면 원소를 몇 개 더 처리한 후 제한된 개수 이후의 결과를 버려도 아무런 해가 없다고 가정한다.
- 스트림을 잘못 병렬화하면 (응답 불가를 포함해) 성능이 나빠질 뿐만 아니라 결과 자체가 잘못되거나 예상 못한 동작이 발생할 수있다.
- Stream 명세는 이때 사용되는 함수 객체에 관한 엄중한 규약을 정의해놨습니다. 예를 들어 Stream reduce 연산에 건네지는 accumulator와 combiner 함수느 반드시 결합법칙을 만족하고, 간섭받지 않고, 상태를 갖지 않아야 한다.
- 스트림 병렬화는 오직 성능 최적화 수단입니다.
- 따라서 성능 테스트를 통해 가치가 있는지 확인해야 합니다.
- 보통은 병렬 스트림 파이프라인도 공통의 포크-조인 풀에서 수행(같은 스레드 풀 사용)되므로 잘못된 파이프라인 하나가 다른 부분의 성능에까지 악영향을 미칠수 있다.
- 조건이 잘 갖춰지면 parallel 메서드 호출 하나로 거의 프로세서 코어 수에 비례하는 성능 향상을 만끽할 수 있다.
- 스트림의 소스가 ArrayList, HashMap, HashSet, ConcurrentHashMap의 인스턴스나 배열, int, long 범위일 때 병렬화의 효과가 가장 좋다.
- 그 이유는 아래와 같다.
- 자료구조들은 모두 데이터를 원하는 크기로 정확하고 손쉽게 나눌수 있어 일을 다수의 스레드에 분배하기 좋다.
- 나누는 작업은 Spliterator가 담당하며, Spliterator 객체는 Stream 이나, Iterable의 spliterator 메소드로 얻어올수 있다.
- 순차적으로 실행할 떄 참조 지역성이 뛰어나다.
- 참조 지역성이란 메모리에 연속으로 저장되어 있다는 의미
- 가장 뛰어난 자료구조는 기본타입의 배열이다.
- 자료구조들은 모두 데이터를 원하는 크기로 정확하고 손쉽게 나눌수 있어 일을 다수의 스레드에 분배하기 좋다.
- 단일 쓰레드에서 사용하기 위해 만들어졌다.
- 무작위 수를 위한 스트림 병렳화를 위해 설계되었다.
- 모든 연산을 동기화 하기 떄문에 최악의 연산을 자랑한다.
- 스트림을 잘못 병렬화하면 (응답 불가를 포함해) 성능이 나빠질 뿐만 아니라 결과 자체가 잘못되거나 예상 못한 동작이 발생할 수있다.
- 데이터 소스가 Stream.iterate거나 중간 연산으로 limit를 쓰면 파이프라인 병렬화로는 성능 개선을 기대할 수 없다.
- 스트림의 소스가 ArrayList, HashMap, HashSet, ConcurrentHashMap의 인스턴스나 배열, int, long 범위일 때 병렬화의 효과가 가장 좋다.
- 단순히 성능이 빨라질꺼라는 확신 없이 파이프라인 병렬화는 시도조차 하지 말아라.