기술 학습

JAVA 병렬 처리(Parallel Processing)와 ForkJoinPool 활용

EpicArts 2025. 2. 18. 19:14

1. 병렬 처리란?

병렬 처리(Parallel Processing)는 여러 개의 작업을 동시에 수행하여 프로그램의 성능을 극대화하는 기법입니다. 멀티코어 프로세서 환경에서는 병렬 처리를 활용하면 연산 속도를 크게 향상시킬 수 있습니다.

병렬 처리의 주요 특징:

  • 작업을 여러 개의 코어에서 동시에 실행하여 성능 향상
  • 멀티스레딩과 다르게 작업을 더 효율적으로 분배 가능
  • Fork/Join 프레임워크를 활용하여 쉽게 구현 가능

2. Java에서 병렬 처리 구현 방법

2.1 parallelStream()을 활용한 병렬 처리

Java 8부터 컬렉션 프레임워크에서는 parallelStream()을 제공하여 간단하게 병렬 처리를 수행할 수 있습니다.

import java.util.Arrays;
import java.util.List;

public class ParallelStreamExample {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry");
        list.parallelStream().forEach(System.out::println);
    }
}

parallelStream()을 활용하면 내부적으로 ForkJoinPool을 사용하여 병렬 실행됩니다.


2.2 ForkJoinPool을 활용한 병렬 처리

ForkJoinPool은 큰 작업을 작은 단위로 나누어 병렬로 실행할 수 있는 Java의 병렬 프레임워크입니다.

2.2.1 ForkJoinTask 정의하기

import java.util.concurrent.RecursiveTask;

public class ForkJoinSum extends RecursiveTask<Long> {
    private final long[] numbers;
    private final int start, end;
    private static final int THRESHOLD = 1000;

    public ForkJoinSum(long[] numbers, int start, int end) {
        this.numbers = numbers;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        if (end - start <= THRESHOLD) {
            long sum = 0;
            for (int i = start; i < end; i++) {
                sum += numbers[i];
            }
            return sum;
        }
        int mid = (start + end) / 2;
        ForkJoinSum leftTask = new ForkJoinSum(numbers, start, mid);
        ForkJoinSum rightTask = new ForkJoinSum(numbers, mid, end);
        leftTask.fork();
        return rightTask.compute() + leftTask.join();
    }
}

2.2.2 ForkJoinPool 실행하기

import java.util.concurrent.ForkJoinPool;

public class ForkJoinExample {
    public static void main(String[] args) {
        long[] numbers = new long[10_000];
        for (int i = 0; i < numbers.length; i++) {
            numbers[i] = i + 1;
        }
        
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinSum task = new ForkJoinSum(numbers, 0, numbers.length);
        long sum = pool.invoke(task);
        
        System.out.println("합계: " + sum);
    }
}

ForkJoinPool은 작업을 자동으로 분할하고 병렬로 실행하여 성능을 최적화합니다.


3. CompletableFuture를 활용한 병렬 처리

CompletableFuture를 활용하면 여러 비동기 작업을 병렬로 실행하고 그 결과를 쉽게 병합할 수 있습니다.

import java.util.concurrent.CompletableFuture;

public class CompletableFutureParallelExample {
    public static void main(String[] args) {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            return 10;
        });
        
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            return 20;
        });
        
        CompletableFuture<Integer> result = future1.thenCombine(future2, Integer::sum);
        System.out.println("결과: " + result.join());
    }
}

thenCombine()을 활용하면 두 개의 비동기 작업을 병렬로 실행하고 결과를 합칠 수 있습니다.


4. 병렬 처리 성능 비교

방법사용 방식장점단점

parallelStream() 컬렉션 데이터 병렬 처리 쉬운 사용법 작은 데이터에서는 성능 향상 미미
ForkJoinPool 큰 작업을 나누어 병렬 처리 성능 최적화 가능 복잡한 코드 필요
CompletableFuture 비동기 작업을 병렬 실행 병렬 처리 후 조합 가능 예외 처리 복잡

✅ 상황에 맞는 병렬 처리 방식을 선택하면 성능을 최적화할 수 있습니다.


5. 결론

이번 글에서는 Java에서 병렬 처리를 구현하는 다양한 방법을 살펴보았습니다. parallelStream(), ForkJoinPool, 그리고 CompletableFuture를 적절히 활용하면 대량의 데이터를 빠르게 처리할 수 있습니다.

다음 글에서는 Java의 멀티스레딩(Multithreading)과 동기화(Synchronization)에 대해 알아보겠습니다!