fork & join 프레임웍

2022. 5. 10. 13:07·자바/프로세스와 쓰레드

1. fork & join 프레임웍

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

만들어 준다.

 

- 수행할 적업에 따라 두 클래스로 나눠지는데, 상속받아 구현해야 한다.

클래스 설명
RecursiveAction 반환값이 없는 작업을 구현할 때 사용
RecursiveTask 반환값이 있는 작업을 구현할 때 사용

 

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

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

 

 

- fork()는 해당 작업을 쓰레드의 작업 큐에 넣는 작업을 한다. 

( 비동기 메서드)

 

- join()은 해당 작업의 수행이 끝날 때까지 기다렸다가, 수행이 끝나면 그 결과를 반환한다.

( 동기 메서드 )

 

- invoke()는 쓰레드 풀과 수행할 작업이 생성되면 작업을 시작하는 메서드이다.

 

- 쓰레드의 start()와 run()이 있다면, fork&join 프레임웍은 invoke()와 compute()가 있는 것이다.

 

 

 

 

1-1. fork & join 프레임웍을 이해하기 위한 예제(1)

: from에서 to까지 모두 더하는 예제를 쓰레드 풀을 만들어 작업을 나눈 예제이다.

 

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

public class Exercise021 {
	public static void main(String[] args) throws Exception {
		ForkJoinPool pool = new ForkJoinPool();
		//쓰레드 풀 생성
		
		SumTask task = new SumTask(1, 10);
		//수행할 작업을 생성
		
		Long result = pool.invoke(task);
		//invoke()를 호출해서 작업을 시작
		
		System.out.println(result);
		int sum = 0;
	}
}

class SumTask extends RecursiveTask<Long>{
	long from, to;
	
	public SumTask(long from, long to) {
		this.from = from;
		this.to = to;
	}
	
	public long sum() {
		int sum = 0;
		
		for(long i = from; i<=to; i++) {
			sum += i;
		}
		return sum;
	}
	
	@Override
	protected Long compute() {
		long size = to - from +1;
		
		if(size <= 5) {
			return sum();
		}
		long half = (from+to)/2;
		
		SumTask leftSum = new SumTask(from, half);
		SumTask rightSum = new SumTask(half+1, to);
		
		leftSum.fork();
		// 작업 (leftSum)을 작업 큐에 넣는다.
		return rightSum.compute() + leftSum.join();
		}
}

 

( 나눠진 작업은 각 쓰레드가 골고루 나눠서 처리한다. )

 

( fork()는 비동기 메서드이므로, 호출하면 결과를 기다리지 않고 바로 다음 구문이 실행된다. )

( 그러다가 작업을 더이상 나눌 수 없게 되었을 때, compute()의 재귀호출이 끝나고 join()의 결과를 기다렸다가

더해서 결과를 반환한다. )

 

 

 

 

1-1. fork & join 프레임웍을 이해하기 위한 예제(1)

: 일반 for문과 fork&join 프레임웍의 성능을 비교하는 예제이다.

 

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

public class Exercise022 {
	static final ForkJoinPool pool = new ForkJoinPool();  // 쓰레드풀을 생성

	public static void main(String[] args) {
		long from = 1L;
		long to   = 100_000_000L;

		SumTask2 task = new SumTask2(from, to);

		long start = System.currentTimeMillis(); // 시작시간 초기화
		Long result = pool.invoke(task);

		System.out.println("Elapsed time(4 Core):"+(System.currentTimeMillis()-start));
		System.out.printf("sum of %d~%d=%d%n", from, to, result);
		System.out.println();

		result = 0L;
		start = System.currentTimeMillis(); // 시작시간 초기화
		for(long i=from;i<=to;i++)
			result += i;

		System.out.println("Elapsed time(1 Core):"+(System.currentTimeMillis()-start));
		System.out.printf("sum of %d~%d=%d%n", from, to, result);
	} // main의 끝
}

class SumTask2 extends RecursiveTask<Long> {
	long from;
	long to;

	SumTask2(long from, long to) {
		this.from = from;
		this.to    = to;
	}

	public Long compute() {
		long size = to - from;

		if(size <= 5)     // 더할 숫자가 5개 이하면
			return sum(); // 숫자의 합을 반환

		long half = (from+to)/2;

		// 범위를 반으로 나눠서 두 개의 작업을 생성
		SumTask2 leftSum  = new SumTask2(from, half);
		SumTask2 rightSum = new SumTask2(half+1, to);

		leftSum.fork();

		return rightSum.compute() + leftSum.join();
	}

	long sum() { // from~to의 모든 숫자를 더한 결과를 반환
		long tmp = 0L; 

		for(long i=from;i<=to;i++)
			tmp += i;

		return tmp;
	}
}

 

( for문보다 fork()&join 프레임웍으로 계산한 것이 더 빠르게 측정되었다. )

( 책에서는 옛날이다보니 for문이 더 빠르게 나왔지만, 현재 멀티 쓰레드로 처리하는 것이 더 빠르다. )

 

( 그러므로, 상황에 맞게 더 좋은 성능을 택하는 것이 좋다. )

 

 

 

 

2. 다른 쓰레드의 작업 훔쳐오기

- fork()가 호출되어 작업 큐에 추가된 작업 역시, compute()에 의해 더 이상 나눠지지 않을 때까지 반복해서 

나뉘고, 자신의 작업 큐가 비어있는 쓰레드는 다른 쓰레드의 작업 큐에서 작업을 가져와서 수행한다.

이러한 과정을 '작업 훔쳐오기' 라고 하며, 모두 쓰레드풀에 의해 자동적으로 이루어진다.

저작자표시 (새창열림)

'자바 > 프로세스와 쓰레드' 카테고리의 다른 글

volatile  (0) 2022.05.10
쓰레드의 동기화(3): Lock과 Condition을 이용한 동기화  (0) 2022.05.08
쓰레드의 동기화(2): wait() & notify()  (0) 2022.05.07
쓰레드의 동기화(1): Critical Section & Lock, snychronized  (0) 2022.05.07
쓰레드의 실행제어  (0) 2022.02.20
'자바/프로세스와 쓰레드' 카테고리의 다른 글
  • volatile
  • 쓰레드의 동기화(3): Lock과 Condition을 이용한 동기화
  • 쓰레드의 동기화(2): wait() & notify()
  • 쓰레드의 동기화(1): Critical Section & Lock, snychronized
백_곰
백_곰
  • 백_곰
    친절한 코딩
    백_곰
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 알고리즘 (with JAVA)
        • 기본 알고리즘
        • 완전 탐색
        • 분할 정복 알고리즘
        • 동적 계획법
        • 탐욕법
        • 코딩 테스트 기출 문제
        • 코드트리 조별과제
      • 백준 (with JAVA)
        • 완전 탐색
        • 분할 정복
        • 그 외
      • 자바
        • 개발 환경 구축하기
        • 팁
        • 기본적인 개념
        • 컬렉션 프레임워크
        • 프로세스와 쓰레드
        • 지네릭스
        • 람다식
        • 스트림
        • 입출력 IO
        • 네트워킹
        • 열거형(enums)
        • java.lang 패키지
        • java.time 패키지
        • 유용한 클래스들
        • 형식화 클래스들
      • 안드로이드 with 자바
        • 응용 문제들
        • 자잘한 문제들
        • 오류 보고서
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      map()
      serializable
      코딩트리조별과제
      ServerSocket
      소켓 프로그래밍
      불안정 정렬
      안정 정렬
      코드트리
      스트림
      안드로이드 스튜디오
      java.time 패키지
      TCP 소켓 프로그래밍
      Collections Framework
      유용한 클래스
      코딩테스트
      java.lang패키지
      InputStream
      중간연산
      Arrays
      문자 기반 스트림
      선택 정렬
      file
      제자리 정렬
      다형성
      snail
      알고스팟
      역직렬화
      자바 개념
      outputstream
      람다식
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.3
    백_곰
    fork & join 프레임웍
    상단으로

    티스토리툴바