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 이하일 때까지 재귀적으로 분할하지 않고 직접 계산하도록 한다.