자바 병발 중인 포크/Join 프레임 워 크 메커니즘 상세 설명

9255 단어 JavaForkJoin프레임
포크/조 인 트 프레임 이란 무엇 입 니까?
포크/Join 프레임 워 크 는 JDk 7 에 도 입 된 스 레 드 풀 로 하나의 큰 임 무 를 여러 개의 작은 임무 로 분해 하여 병행 하여 수행 하 며,최종 적 으로 모든 작은 임무 결 과 를 모 아 큰 임무 결 과 를 얻 는 특수 임무 입 니 다.이름 을 통 해 알 수 있 듯 이 프레임 워 크 는 주로 Fork 와 Join 두 단계 로 나 뉘 는데 첫 번 째 단 계 는 Fork 는 하나의 큰 임 무 를 여러 개의 하위 임무 로 나 누 어 병행 하 는 것 이 고 두 번 째 단 계 는 Join 은 이 하위 임무 의 모든 수행 결 과 를 합 쳐 큰 임 무 를 얻 은 결과 이다.
여기 서 주요 절 차 를 발견 하기 어렵 지 않다.먼저 하나의 임무 가 충분 한 지 판단 하고 임무 가 충분 하면 직접 계산한다.그렇지 않 으 면 몇 개의 더 작은 임무 로 나 누 어 각각 계산한다.이 과정 은 일련의 작은 임무 로 반복 적 으로 나 눌 수 있다.포크/Join 프레임 워 크 는 분할 통치하 다 을 기반 으로 한 알고리즘 으로 큰 임 무 를 나 누 어 여러 개의 독립 된 작은 임 무 를 수행 한 다음 에 이 작은 임 무 를 병행 하여 작은 임 무 를 합 친 결과 큰 임무 의 최종 결 과 를 얻 고 병행 계산 을 통 해 효율 을 높 인 다.
Fork/Join 프레임 워 크 사용 예시
다음은 계산 목록 에 있 는 모든 요소 의 총 화 를 예 로 들 어 Fork/Join 프레임 워 크 가 어떻게 사용 되 는 지 살 펴 보 겠 습 니 다.전체적인 사 고 는 이 목록 을 여러 하위 목록 으로 나 눈 다음 에 모든 하위 목록 의 요 소 를 구 한 다음 에 우 리 는 이 값 의 총 화 를 계산 하면 원시 목록 의 합 을 얻 을 수 있 습 니 다.포크/Join 프레임 워 크 에서 포크 Jointask 를 정의 하여 포크/Join 작업 을 표시 합 니 다.포크(),join()등 작업 을 제공 합 니 다.일반적인 상황 에서 저 희 는 이 포크 Jointask 클래스 를 직접 계승 하지 않 고 프레임 워 크 로 제공 하 는 두 개의 포크 Jointask 하위 클래스 를 사용 합 니 다.
  • RecursiveAction 은 결 과 를 되 돌려 주지 않 은 Fork/Join 작업 을 표시 하 는 데 사 용 됩 니 다.
  • RecursiveTask 는 결 과 를 되 돌려 주 는 Fork/Join 작업 에 사 용 됩 니 다.
  • 분명 한 것 은 이 예제 에서 결 과 를 되 돌려 야 합 니 다.SumAction 류 는 Recursive Task 에서 계승 되 고 코드 가 들 어 오 는 것 을 정의 할 수 있 습 니 다.
    
    /**
     * @author mghio
     * @since 2021-07-25
     */
    public class SumTask extends RecursiveTask<Long> {
    
      private static final int SEQUENTIAL_THRESHOLD = 50;
    
      private final List<Long> data;
    
      public SumTask(List<Long> data) {
        this.data = data;
      }
    
      @Override
      protected Long compute() {
        if (data.size() <= SEQUENTIAL_THRESHOLD) {
          long sum = computeSumDirectly();
          System.out.format("Sum of %s: %d
    ", data.toString(), sum); return sum; } else { int mid = data.size() / 2; SumTask firstSubtask = new SumTask(data.subList(0, mid)); SumTask secondSubtask = new SumTask(data.subList(mid, data.size())); // firstSubtask.fork(); secondSubtask.fork(); // , long firstSubTaskResult = firstSubtask.join(); long secondSubTaskResult = secondSubtask.join(); return firstSubTaskResult + secondSubTaskResult; } } private long computeSumDirectly() { long sum = 0; for (Long l : data) { sum += l; } return sum; } public static void main(String[] args) { Random random = new Random(); List<Long> data = random .longs(1_000, 1, 100) .boxed() .collect(Collectors.toList()); ForkJoinPool pool = new ForkJoinPool(); SumTask task = new SumTask(data); pool.invoke(task); System.out.println("Sum: " + pool.invoke(task)); } }
    목록 크기 가 SEQUENTIAL 보다 작 을 때THRESHOLD 변수의 값(한도 값)을 작은 작업 으로 보고 구 와 목록 요소 의 결 과 를 직접 계산 합 니 다.그렇지 않 으 면 다시 작은 작업 으로 나 누 어 실행 결 과 는 다음 과 같 습 니 다.

    이 예제 코드 를 통 해 알 수 있 듯 이 Fork/Join 프레임 워 크 에서 ForkJoinTask 작업 과 일반적인 일반적인 작업 의 주요 차이 점 은 다음 과 같다.ForkJoinTask 는 추상 적 인 방법 coptute()를 실현 하여 계산 논 리 를 정의 해 야 한다.이 방법 에서 일반적으로 통용 되 는 실현 템 플 릿 은 먼저 현재 작업 이 작은 작업 인지 아 닌 지 를 판단 하고 만약 그렇다면 임 무 를 수행 해 야 한다.작은 작업 이 아니라면 두 개의 하위 작업 으로 나 누 어 진 다음 각 하위 작업 이 fork()방법 을 호출 할 때 compute()방법 에 다시 들 어가 현재 작업 을 하위 작업 으로 나 눌 필요 가 있 는 지 확인 합 니 다.작은 작업 이 라면 현재 임 무 를 수행 하고 결 과 를 되 돌려 줍 니 다.그렇지 않 으 면 계속 분할 합 니 다.마지막 으로 join()방법 을 호출 하여 모든 하위 작업 이 완 료 될 때 까지 기다 리 고 실행 결 과 를 얻 습 니 다.의사 코드 는 다음 과 같 습 니 다:
    
    if (problem is small) {
      directly solve problem.
    } else {
      Step 1. split problem into independent parts.
      Step 2. fork new subtasks to solve each part.
      Step 3. join all subtasks.
      Step 4. compose result from subresults.
    }
    포크/Join 프레임 디자인
    포크/Join 프레임 워 크 의 핵심 사상 은 하나의 큰 임 무 를 여러 개의 작은 임무 로 나 눈 다음 에 모든 작은 임무 의 결 과 를 모 아 큰 임무 의 결 과 를 얻 는 것 입 니 다.만약 에 이런 프레임 워 크 를 설계 하 라 고 하면 어떻게 실현 할 것 입 니까?생각해 보 세 요)포크/Join 프레임 워 크 의 전체 절 차 는 그 이름 에서 보 듯 이 두 단계 로 나 뉜 다.
  • 대 임 무 를 분할 하려 면 이런 종류 가 필요 합 니 다.큰 임 무 를 하위 임무 로 나 누 는 데 사용 해 야 합 니 다.한 번 에 분 리 된 하위 임 무 는 비교적 클 수 있 습 니 다.여러 번 분 리 를 해 야 합 니 다.분 리 된 하위 임 무 는 우리 가 정의 한 작은 임무 에 부합 되 어야 끝 납 니 다.
  • 작업 을 수행 하고 작업 결 과 를 통합 하 는 첫 번 째 단계 로 분 리 된 하위 작업 은 각각 양 끝 대기 열 에 저 장 됩 니 다(P.S.여기 서 왜 쌍 단 대기 열 을 사용 하 는 지 아래 글 을 보십시오).그리고 각 대기 열 에서 하나의 스 레 드 를 시작 하여 대기 열 에서 작업 수행 을 가 져 옵 니 다.이 하위 작업 의 실행 결 과 는 하나의 통 일 된 대기 열 에 놓 인 다음 하나의 스 레 드 를 시작 하여 이 대기 열 에서 데 이 터 를 가 져 오고 마지막 으로 이 데 이 터 를 합 쳐 되 돌려 줍 니 다.
  • 포크/Join 프레임 워 크 는 다음 과 같은 두 가지 종 류 를 사용 하여 상기 두 가지 절 차 를 완성 합 니 다.
  • ForkJointask 류 는 위의 인 스 턴 스 에서 도 언급 되 었 습 니 다.ForkJoin 작업 은 프레임 워 크 를 사용 할 때 먼저 작업 을 정의 해 야 합 니 다.보통 ForkJointask 류 의 하위 클래스 RecursiveAction(결과 반환 없 음)이나 RecursiveTask(결과 반환 있 음)만 계승 하면 됩 니 다.
  • ForkJoinPool 은 이름 에서 도 1,2 를 알 수 있 습 니 다.바로 ForkJoinTask 를 수행 하 는 스 레 드 풀 입 니 다.큰 작업 에서 분 리 된 하위 작업 은 현재 스 레 드 의 양 끝 대기 열의 머리 에 추 가 됩 니 다.
  • 생각 하 기 를 좋아 하 는 당신 은 마음속 으로 이런 장면 을 생각 할 것 입 니 다.우리 가 큰 임 무 를 완성 해 야 할 때 이 큰 임 무 를 여러 개의 독립 된 하위 임무 로 나 눌 것 입 니 다.이 하위 임 무 는 독립 된 대열 에 놓 고 모든 대열 에 하나의 단독 라인 을 만들어 대열 안의 임 무 를 수행 합 니 다.즉,이 스 레 드 와 대열 의 일대일 관 계 를 수행 합 니 다.그러면 어떤 스 레 드 가 먼저 자신의 대열 의 임 무 를 완성 할 수 있 고 어떤 스 레 드 는 완성 되 지 않 았 기 때문에 먼저 임 무 를 수행 한 스 레 드 가 기다 리 는 것 이 좋 은 문제 입 니 다.
    병발 한 이상 컴퓨터 의 성능 을 최대한 압착 해 야 한다.이런 장면 에 대해 병발 대사 Doug Lea작업 절취 알고리즘 처 리 를 사 용 했 고 작업 도 난 알고리즘 을 사용 한 후에 자신의 대열 에서 임 무 를 수행 하 는 스 레 드 를 먼저 완성 하면 다른 스 레 드 의 대열 에서'하나의 임 무 를 훔 쳐 서 집행 한다.하하,한 측 이 어렵 고 팔방 이 지원 한다.그러나 이 스 레 드 와 대기 열의 보유 스 레 드 는 같은 대기 열 에 동시에 접근 하기 때문에 훔 친 작업 의 스 레 드 와 도 둑 맞 은 작업 의 스 레 드 간 의 경쟁 을 줄 이기 위해 ForkJoin 은 쌍 단 대기 열 이라는 데이터 구 조 를 선택 하면 이러한 규칙 에 따라 임 무 를 수행 할 수 있 습 니 다.도 둑 맞 은 작업 의 스 레 드 는 항상 대기 열 머리 에서 임 무 를 가 져 오고 수행 할 수 있 습 니 다.작업 을 훔 치 는 스 레 드 는 대기 열 끝 에서 작업 을 수행 하 는 데 사 용 됩 니 다.이 알고리즘 은 대부분 상황 에서 다 중 스 레 드 를 충분히 이용 하여 병행 계산 을 할 수 있 지만 양 단 대기 열 에 하나의 임무 만 있 는 등 극단 적 인 상황 에서 어느 정도 경쟁 이 존재 할 수 있 습 니 다.

    포크/Join 프레임 워 크 실현 원리
    Fork/Join 프레임 워 크 의 실현 핵심 은 ForkJoinPool 류 입 니 다.이 유형의 중요 한 구성 부분 은 ForkJoinTask 배열 과 ForkJoinWorkerThread 배열 입 니 다.그 중에서 ForkJoinTask 배열 은 프레임 워 크 사용자 가 ForkJoinPool 에 제출 한 임 무 를 저장 하고 ForkJoinWorkerThread 배열 은 이 임 무 를 수행 합 니 다.작업 은 다음 과 같은 네 가지 상태 가 있 습 니 다.
    NORMAL 이 완료 되 었 습 니 다.
    CANCELLED 가 취소 되 었 습 니 다.
    SIGNAL 신호
    예외 적 으로 이상 발생
    다음은 이 두 가지 핵심 방법의 실현 원 리 를 살 펴 보 겠 습 니 다.먼저 ForkJointask 의 fork()방법 을 살 펴 보 겠 습 니 다.소스 코드 는 다음 과 같 습 니 다.

    방법 은 ForkJoinWorkerThread 형식의 스 레 드 에 대해 먼저 ForkJoinWorkerThread 의 work Queue 의 push()방법 으로 이 작업 을 비동기 적 으로 수행 한 다음 결 과 를 되 돌려 줍 니 다.ForkJoinPool 의 push()방법 을 계속 따라 갑 니 다.원본 코드 는 다음 과 같 습 니 다.

    방법 은 현재 작업 을 ForkJoinTask 작업 대기 열 배열 에 추가 한 다음 ForkJoinPool 의 signal Work 방법 으로 작업 스 레 드 를 만 들 거나 깨 워 서 이 작업 을 수행 합 니 다.그리고 ForkJointask 의 join()방법 을 살 펴 보 겠 습 니 다.방법 원본 은 다음 과 같 습 니 다.


    방법 은 먼저 doJoin()방법 을 호출 했 습 니 다.이 방법 은 현재 작업 의 상 태 를 되 돌려 주 고 돌아 오 는 작업 상태 에 따라 다른 처 리 를 합 니 다.
  • 이미 완 성 된 상 태 는 바로 결과
  • 으로 돌아 갑 니 다.
  • 취 소 된 상 태 는 바로 이상 을 던 집 니 다(CancellationException)
  • 이상 상태 가 발생 하면 해당 하 는 이상 을 직접 던 집 니 다
  • doJoin()방법 을 계속 따라 갑 니 다.방법 원본 은 다음 과 같 습 니 다.

    방법 은 우선 현재 작업 상태 가 이미 완성 되 었 는 지 판단 하고,실행 이 완료 되면 바로 작업 상태 로 돌아 갑 니 다.수행 이 완료 되 지 않 으 면 작업 배열(work Queue)에서 작업 을 꺼 내 수행 하고,작업 수행 이 완료 되면 작업 상 태 를 NORMAL 로 설정 하 며,이상 이 발생 하면 이상 을 기록 하고 작업 상 태 를 EXCEPTIONAL(doExec()방법 에서)로 설정 합 니 다.
    총결산
    본 고 는 자바 병발 프레임 워 크 의 포크/Join 프레임 워 크 의 기본 원리 와 그 가 사용 하 는 작업 절취 알고리즘(work-stealing),디자인 방식 과 부분 실현 소스 코드 를 소개 한다.Fork/Join 프레임 워 크 는 JDK 의 공식 표준 라 이브 러 리 에서 도 적용 된다.예 를 들 어 JDK 1.8+표준 라 이브 러 리 가 제공 하 는 Arrays.parallelsert(array)는 병렬 정렬 을 할 수 있 습 니 다.그 원 리 는 내부 에서 Fork/Join 프레임 워 크 를 통 해 큰 배열 을 병렬 정렬 하면 정렬 의 속 도 를 높 일 수 있 고 집합 중의 Collection.parallels트림()의 바 텀 도 Fork/Join 프레임 워 크 를 바탕 으로 이 루어 집 니 다.마지막 으로 작은 작업 의 한도 값 을 정의 하 는 것 은 테스트 검증 을 통 해 합 리 적 으로 제시 하고 프로그램 이 가장 좋 은 성능 을 얻 을 수 있 도록 하 는 것 이다.
    자바 병발 중인 포크/조 인 트 프레임 워 크 메커니즘 에 대한 자세 한 설명 은 여기까지 입 니 다.자바 포크/조 인 트 프레임 워 크 에 관 한 더 많은 내용 은 저희 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 응원 부탁드립니다!

    좋은 웹페이지 즐겨찾기