Fork/Join 프레임워크 실전 활용 예제

Fork/Join 프레임워크를 활용한 계산 예제

Fork/Join 프레임워크는 Java에서 병렬 처리를 구현하는 데 강력한 도구이다. 이 프레임워크는 작업을 작은 하위 작업으로 분할(fork)하고, 각 하위 작업의 결과를 결합(join)하는 방식으로 작동한다. 특히 재귀적인 알고리즘에서 그 진가를 발휘한다.

예제 1: 피보나치 수열 n번째 항 계산

피보나치 수열은 대표적인 선형递推 数열로, 세 번째 항부터는 앞의 두 항의 합으로 정의된다.

수열: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89...

수학적 정의: f(n) = f(n-1) + f(n-2)

import org.junit.Test;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;

public class FibonacciTest {
    
    class FibonacciCalculator extends RecursiveTask<Integer> {
        private final int number;

        public FibonacciCalculator(int number) {
            this.number = number;
        }

        @Override
        protected Integer compute() {
            if (number <= 1) {
                return number;
            } else {
                FibonacciCalculator taskA = new FibonacciCalculator(number - 1);
                FibonacciCalculator taskB = new FibonacciCalculator(number - 2);
                
                taskA.fork();
                taskB.fork();
                
                return taskA.join() + taskB.join();
            }
        }
    }

    @Test
    public void calculateFibonacci() throws ExecutionException, InterruptedException {
        ForkJoinPool threadPool = new ForkJoinPool();
        System.out.println("사용 가능한 CPU 코어 수: " + Runtime.getRuntime().availableProcessors());
        
        long startTime = System.currentTimeMillis();
        FibonacciCalculator calculator = new FibonacciCalculator(40);
        Future<Integer> result = threadPool.submit(calculator);
        
        System.out.println("계산 결과: " + result.get());
        
        long endTime = System.currentTimeMillis();
        System.out.println("소요 시간: " + (endTime - startTime) + " 밀리초");
    }
}

실행 결과:

사용 가능한 CPU 코어 수: 4
계산 결과: 102334155
소요 시간: 9500 밀리초

예제 2: 1부터 4까지의 합 계산

이번에는 Fork/Join 프레임워크를 활용하여 간단한 합산 문제를 해결해보자. 1+2+3+4의 결과를 구하기 위해 작업을 분할한다.

분할 기준값(threshold)을 2로 설정하면, 4개의 숫자를 두 개의 하위 작업으로 나눌 수 있다:

  • 하위 작업 1: 1 + 2 계산
  • 하위 작업 2: 3 + 4 계산

각 하위 작업의 결과를 결합하여 최종 결과를 얻는다. 결과를 반환해야 하므로 RecursiveTask를 상속해야 한다.

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;

public class ForkJoinCalculationDemo {

    public static void main(String[] args) {
        ForkJoinPool pool = new ForkJoinPool();
        SumTask task = new SumTask(1, 4);
        Future<Integer> result = pool.submit(task);
        
        try {
            System.out.println("계산 결과 = " + result.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class SumTask extends RecursiveTask<Integer> {
    private static final long serialVersionUID = 123456789L;
    private static final int THRESHOLD = 2;
    
    private final int start;
    private final int end;

    public SumTask(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Integer compute() {
        int total = 0;
        
        boolean canCalculate = (end - start) <= THRESHOLD;
        
        if (canCalculate) {
            for (int i = start; i <= end; i++) {
                total += i;
            }
        } else {
            int middle = (start + end) / 2;
            SumTask leftTask = new SumTask(start, middle);
            SumTask rightTask = new SumTask(middle + 1, end);
            
            leftTask.fork();
            rightTask.fork();
            
            int leftResult = leftTask.join();
            int rightResult = rightTask.join();
            
            total = leftResult + rightResult;
        }
        
        return total;
    }
}

이 예제에서 THRESHOLD值为2로 설정하여, 작업 크기가 2 이하일 때까지 재귀적으로 분할하지 않고 직접 계산하도록 한다.

태그: java concurrency forkjoin parallel-processing recursivetask

7월 26일 15:47에 게시됨