본문 바로가기

기술 학습

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

728x90

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)에 대해 알아보겠습니다!


728x90