병렬 Stream(parallelStream) 주의점 완벽 이해하기
많은 개발자가 처음 병렬 Stream을 보면 감탄한다.
list.stream()
를
list.parallelStream()
으로만 바꾸면
멀티코어 사용
↓
성능 향상
이 될 것 같기 때문이다.
하지만 실무에서는 오히려:
parallelStream() 때문에
성능이 더 느려지는 경우
가 정말 많다.
1. Stream vs parallelStream
일반 Stream
list.stream()
병렬 Stream
list.parallelStream()
차이
stream()
↓
순차 실행
parallelStream()
↓
병렬 실행
2. 예제
list.parallelStream()
.forEach(System.out::println);
내부적으로
ForkJoinPool
사용.
3. 실제 동작
100만 건 데이터.
1 ~ 1,000,000
parallelStream
1~250000
250001~500000
500001~750000
750001~1000000
자동 분할.
4. 그럼 무조건 빠른가?
아니다.
5. 병렬 처리 비용 존재
ForkJoinPool은:
작업 분할
Thread 할당
Context Switching
결과 병합
비용 발생.
6. 작은 작업
예:
list.parallelStream()
.map(x -> x + 1)
데이터
100개
결과
병렬 처리 비용
>
실제 작업 비용
오히려 느려짐.
7. 대표적인 실수
users.parallelStream()
.map(User::getName)
.toList();
데이터
100건
거의 무조건 손해.
8. 언제 효과가 좋을까?
작업량이 클 때.
예:
수백만 건
복잡한 계산
CPU 많이 사용
9. CPU Bound 작업
대표.
암호화
압축
영상 처리
이미지 처리
수학 계산
병렬 Stream 효과 좋음.
10. 예시
numbers.parallelStream()
.map(this::heavyCalculation)
.toList();
CPU 사용량 높음.
↓
병렬 처리 효과 큼.
11. I/O Bound 작업
대표.
DB 조회
REST API 호출
파일 읽기
12. 예시
users.parallelStream()
.map(userService::findUser)
.toList();
매우 위험.
13. 왜?
ForkJoinPool Thread가
DB 응답 대기
상태가 됨.
CPU는 놀고 있음.
14. ForkJoinPool 설계 목적
원래
CPU Bound 작업
용.
I/O 작업용 아님.
15. 실무에서 많이 하는 실수
ids.parallelStream()
.map(id -> restTemplate.getForObject(...))
생각.
병렬이니까 빠르겠지?
실제.
Thread 고갈
Connection Pool 부족
응답 지연
16. 순서 문제
일반 Stream.
list.stream()
입력.
1 2 3 4 5
출력.
1 2 3 4 5
17. parallelStream
list.parallelStream()
출력.
3 1 5 2 4
가능.
18. 왜?
여러 Thread가 동시에 처리.
순서 보장 안 함.
19. 예제
list.parallelStream()
.forEach(System.out::println);
결과.
순서 랜덤
20. 순서 보장하려면
forEachOrdered()
예:
list.parallelStream()
.forEachOrdered(
System.out::println
);
21. 문제
순서 보장 비용 발생.
병렬 효과 감소.
22. 공유 객체 문제
매우 중요.
잘못된 코드.
List<Integer> result =
new ArrayList<>();
list.parallelStream()
.forEach(result::add);
23. 문제
ArrayList
Thread-Safe 아님
결과.
데이터 유실
Index 오류
가능.
24. 왜?
여러 Thread가 동시에.
result.add(...)
수행.
Race Condition 발생.
25. 해결
Collector 사용.
좋은 예.
List<Integer> result =
list.parallelStream()
.map(...)
.toList();
Stream이 내부적으로 안전하게 병합.
26. synchronized 사용?
가능.
Collections.synchronizedList(...)
하지만.
Lock 경쟁
발생.
성능 감소.
27. ThreadLocal 문제
실무 중요.
예:
ThreadLocal<UserContext>
사용.
parallelStream
↓
ForkJoinPool Thread 사용.
예상 못한 값 공유 가능.
주의 필요.
28. 트랜잭션 문제
Spring 실무 핵심.
예:
@Transactional
public void process() {
list.parallelStream()
}
29. 문제
Spring Transaction은
ThreadLocal
기반.
30. 병렬 Thread
새 Thread.
기존 트랜잭션 전달 안 됨.
31. 결과
트랜잭션 깨짐
가능.
32. SecurityContext 문제
Spring Security도.
ThreadLocal
사용.
parallelStream 내부.
로그인 정보 없음
가능.
33. ForkJoinPool 문제
매우 중요.
parallelStream은 기본적으로
ForkJoinPool.commonPool()
사용.
34. 공용 Pool
JVM 전체 공유.
parallelStream
CompletableFuture
라이브러리
같은 Pool 사용 가능.
35. 결과
Pool 고갈
가능.
36. 장애 사례
실제 많이 발생.
parallelStream()
남발.
ForkJoinPool Thread 전부 사용
다른 비동기 작업 정체.
37. 병렬 Stream이 적합한 경우
대표.
CPU 계산
대용량 데이터
공유 상태 없음
순서 중요하지 않음
38. 병렬 Stream이 부적합한 경우
대표.
DB 호출
REST API 호출
파일 I/O
트랜잭션 처리
ThreadLocal 사용
39. 실무 추천
DB 호출.
좋은 방법.
CompletableFuture
전용
ThreadPoolExecutor
사용.
40. 예시
CompletableFuture
.supplyAsync(
task,
customExecutor
);
Pool 직접 제어 가능.
41. 면접 단골 질문
Q. parallelStream 내부 구현은?
ForkJoinPool
Q. 무조건 빠른가?
아니다
Q. 어떤 작업에 적합한가?
CPU Bound
Q. DB 조회에도 쓰면 좋은가?
아니다
Q. 순서 보장되는가?
기본은 아니다
Q. Spring @Transactional과 같이 쓰면?
주의 필요
ThreadLocal 기반이라
트랜잭션 전파 안 됨
42. 실무 관점 정리
좋은 예
numbers.parallelStream()
.map(this::encrypt)
.toList();
나쁜 예
ids.parallelStream()
.map(userRepository::findById)
.toList();
더 나쁜 예
list.parallelStream()
.forEach(result::add);
43. 핵심 흐름 요약
parallelStream()
↓
ForkJoinPool
↓
작업 분할
↓
병렬 처리
↓
결과 병합
44. 가장 중요한 핵심 한 줄
parallelStream은 ForkJoinPool 기반의 CPU Bound 병렬 처리에는 효과적이지만, DB/API 호출 같은 I/O 작업이나 ThreadLocal·트랜잭션이 연관된 환경에서는 성능 저하와 예상치 못한 문제를 유발할 수 있다.
45. 동시성 & 멀티스레드 마지막 주제
다음 글은:
Virtual Thread (Java 21)
이다.
이 글을 배우면 지금까지 공부한:
Thread
↓
ThreadPool
↓
ForkJoinPool
↓
CompletableFuture
↓
Virtual Thread
까지 Java 동시성의 발전 흐름이 완성된다.
특히 최근 실무 면접에서는
Virtual Thread가 기존 ThreadPool을 대체할 수 있나요?
질문이 매우 자주 나온다.
'language > java' 카테고리의 다른 글
| Static Factory Method (정적 팩토리 메서드) (0) | 2026.06.01 |
|---|---|
| Virtual Thread (Java 21) 완벽 이해하기 (0) | 2026.06.01 |
| ConcurrentHashMap 동시성 처리 완벽 이해하기 (0) | 2026.06.01 |
| DeadLock(교착상태) 원인과 해결 완벽 이해하기 (0) | 2026.06.01 |
| ReentrantLock 완벽 이해하기 (0) | 2026.06.01 |
댓글