자바의 정석

[자바의 정석 2권] fork & join 프레임웍(1) - Fork&Join 프레임웍이란?

깊게 생각하고 최선을 다하자 2024. 12. 23. 11:54

1) fork & join 프레임웍이란?

- 10년 전까지만 해도 CPU의 속도는 매년 거의 2배씩 빠르게 향상되어 왔다.

  그러나 이제 그 한계에 도달하여 속도보다는 코어의 개수를 늘려서 

  CPU의 성능을 향상시키는 방향으로 발전해가고 있다. 

 

- 이러한 하드웨어의 변화에 발맞춰 프로그래밍도 멀티 코어를 잘 활용할 수 있는 

  멀티쓰레드 프로그래밍이 점점 더 중요해지고 있다. 

  

- 그래서 JDK1.7부터 'fork&join 프레임웍'이 추가되었고,

  이 프레임웍은 하나의 작업을 작은 단위로 나눠서 여러 쓰레드가 동시에 처리하는 것을 

  쉽게 만들어준다.

 

- 먼저 수행할 작업에 따라 RecursiveAction과 RecursiveTask, 

  두 클래스 중에서 하나를 상속받아 구현해야 한다. 

RecursiveAction 반환값이 없는 작업을 구현할 때 사용
RecursiveTask 반환값이 있는 작업을 구현할 때 사용

 

- 두 클래스 모두 compute()라는 추상 메서드를 가지고 있는데, 

  우리는 상속을 통해 추상 메서드를 구현하기만 하면 된다. 

public abstract class RecursiveAction extends ForkJoinTask<Void> {
     ...
     protected abstract void compute(); // 상속을 통해 이 메서드를 구현해야 한다. 
     ...
}


public abstract class RecursiveTask<V> extends ForkJoinTask<V> { 
     ...
     protected abstract V compute(); // 상속을 통해 이 메서드를 구현해야 한다. 
     ...
}

 

- 예를 들어, 1부터 n까지의 합을 계산한 결과를 돌려주는 작업의 구현은 다음과 같이 한다. 

class SumTask extends RecursiveTask<Long> {
   long from, to;
   
   SumTask(long from, long to){
     this.from = from;
     this.to = to; 
   }
   
   public Long compute() {
      // 처리할 작업을 수행하기 위한 문장을 넣는다. 
   }
}

 

- 그 다음에는 쓰레드풀과 수행할 작업을 생성하고, invoke()로 작업을 시작한다.

  쓰레드를 시작할 때 run()이 아니라 start()를 호출하는 것처럼,

  fork&join 프레임웍으로 수행할 작업도 compute()가 아닌 invoke()로 시작한다. 

ForkJoinPool pool = new ForkJoinPool(); // 쓰레드 풀을 생성
SumTask task = new SumTask(from, to); // 수행할 작업을 생성
Long result = pool.invoke(task); // invoke()를 호출해서 작업을 시작

 

- ForkJoinPool은 fork&join 프레임웍에서 제공하는 쓰레드 풀(thread pool)로,

  지정된 수의 쓰레드를 생성해서 미리 만들어 놓고 반복해서 재사용할 수 있게 한다.

  그리고 쓰레드를 반복해서 생성하지 않아도 된다는 장점과

  너무 많은 쓰레드가 생성되어 성능이 저하되는 것을 막아준다는 장점이 있다. 

 

- 쓰레드 풀은 쓰레드가 수행해야 하는 작업이 담긴 큐를 제공하며,

  각 쓰레드는 자신의 작업 큐에 담긴 작업을 순서대로 처리한다.