欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java線程池ForkJoinPool(工作竊取算法)的使用

 更新時(shí)間:2022年11月28日 08:58:55   作者:胡安民  
Fork就是把一個(gè)大任務(wù)切分為若干個(gè)子任務(wù)并行地執(zhí)行,Join就是合并這些子任務(wù)的執(zhí)行結(jié)果,最后得到這個(gè)大任務(wù)的結(jié)果。Fork/Join?框架使用的是工作竊取算法。本文主要介紹了ForkJoinPool的使用,需要的可以參考一下

概述

Fork 就是把一個(gè)大任務(wù)切分為若干個(gè)子任務(wù)并行地執(zhí)行,Join 就是合并這些子任務(wù)的執(zhí)行結(jié)果,最后得到這個(gè)大任務(wù)的結(jié)果。Fork/Join 框架使用的是工作竊取算法。

工作竊取算法

工作竊取算法是指某個(gè)線程從其他隊(duì)列里竊取任務(wù)來(lái)執(zhí)行。對(duì)于一個(gè)比較大的任務(wù),可以把它分割為若干個(gè)互不依賴的子任務(wù),為了減少線程間的競(jìng)爭(zhēng),把這些子任務(wù)分別放到不同的隊(duì)列里,并為每個(gè)隊(duì)列創(chuàng)建一個(gè)單獨(dú)的線程來(lái)執(zhí)行隊(duì)列里的任務(wù),線程和隊(duì)列一一對(duì)應(yīng)。但是,有的線程會(huì)先把自己隊(duì)列里的任務(wù)干完,而其他線程對(duì)應(yīng)的隊(duì)列里還有任務(wù)需要處理,于是它就去其他線程的隊(duì)列里竊取一個(gè)任務(wù)來(lái)執(zhí)行。由于此時(shí)它們?cè)L問(wèn)同一個(gè)隊(duì)列,為了減小競(jìng)爭(zhēng),通常會(huì)使用雙端隊(duì)列。被竊取任務(wù)的線程永遠(yuǎn)從雙端隊(duì)列的頭部獲取任務(wù),竊取任務(wù)的線程永遠(yuǎn)從雙端隊(duì)列的尾部獲取任務(wù)。

工作竊取算法的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):充分利用線程進(jìn)行并行計(jì)算,減少了線程間的競(jìng)爭(zhēng)。

缺點(diǎn):雙端隊(duì)列只存在一個(gè)任務(wù)時(shí)會(huì)導(dǎo)致競(jìng)爭(zhēng),會(huì)消耗更多的系統(tǒng)資源,因?yàn)樾枰獎(jiǎng)?chuàng)建多個(gè)線程和多個(gè)雙端隊(duì)列。

使用 ForkJoinPool 進(jìn)行分叉和合并

ForkJoinPool 在 Java 7 中被引入。它和 ExecutorService 很相似,除了一點(diǎn)不同。ForkJoinPool 讓我們可以很方便地把任務(wù)分成幾個(gè)更小的任務(wù),這些分出來(lái)的任務(wù)也將會(huì)提交給 ForkJoinPool。任務(wù)可以繼續(xù)分割成更小的子任務(wù),只要它還能分割??赡苈?tīng)起來(lái)有些抽象,因此本節(jié)中我們將會(huì)解釋 ForkJoinPool 是如何工作的,還有任務(wù)分割是如何進(jìn)行的。

分叉和合并解釋

在我們開(kāi)始看 ForkJoinPool 之前我們先來(lái)簡(jiǎn)要解釋一下分叉和合并的原理。

分叉和合并原理包含兩個(gè)遞歸進(jìn)行的步驟。兩個(gè)步驟分別是分叉步驟和合并步驟。

分叉

一個(gè)使用了分叉和合并原理的任務(wù)可以將自己分叉(分割)為更小的子任務(wù),這些子任務(wù)可以被并發(fā)執(zhí)行。如下圖所示:

通過(guò)把自己分割成多個(gè)子任務(wù),每個(gè)子任務(wù)可以由不同的 CPU 并行執(zhí)行,或者被同一個(gè) CPU 上的不同線程執(zhí)行。

只有當(dāng)給的任務(wù)過(guò)大,把它分割成幾個(gè)子任務(wù)才有意義。把任務(wù)分割成子任務(wù)有一定開(kāi)銷,因此對(duì)于小型任務(wù),這個(gè)分割的消耗可能比每個(gè)子任務(wù)并發(fā)執(zhí)行的消耗還要大。

什么時(shí)候把一個(gè)任務(wù)分割成子任務(wù)是有意義的,這個(gè)界限也稱作一個(gè)閥值。這要看每個(gè)任務(wù)對(duì)有意義閥值的決定。很大程度上取決于它要做的工作的種類。

合并

當(dāng)一個(gè)任務(wù)將自己分割成若干子任務(wù)之后,該任務(wù)將進(jìn)入等待所有子任務(wù)的結(jié)束之中。一旦子任務(wù)執(zhí)行結(jié)束,該任務(wù)可以把所有結(jié)果合并到同一個(gè)結(jié)果。圖示如下:

當(dāng)然,并非所有類型的任務(wù)都會(huì)返回一個(gè)結(jié)果。如果這個(gè)任務(wù)并不返回一個(gè)結(jié)果,它只需等待所有子任務(wù)執(zhí)行完畢。也就不需要結(jié)果的合并啦。

ForkJoinPool使用

ForkJoinPool 是一個(gè)特殊的線程池,它的設(shè)計(jì)是為了更好的配合 分叉-和-合并 任務(wù)分割的工作。ForkJoinPool 也在 java.util.concurrent 包中,其完整類名為 java.util.concurrent.ForkJoinPool。

創(chuàng)建一個(gè) ForkJoinPool

你可以通過(guò)其構(gòu)造子創(chuàng)建一個(gè) ForkJoinPool。作為傳遞給 ForkJoinPool 構(gòu)造子的一個(gè)參數(shù),你可以定義你期望的并行級(jí)別。并行級(jí)別表示你想要傳遞給 ForkJoinPool 的任務(wù)所需的線程或 CPU 數(shù)量。以下是一個(gè) ForkJoinPool 示例:

// 這個(gè)示例創(chuàng)建了一個(gè)并行級(jí)別為 4 的 ForkJoinPool。   如果是默認(rèn)構(gòu)造會(huì)自動(dòng)識(shí)別當(dāng)前電腦的cup核數(shù)進(jìn)行并行
ForkJoinPool forkJoinPool = new ForkJoinPool(4);

提交任務(wù)到 ForkJoinPool

就像提交任務(wù)到 ExecutorService 那樣,把任務(wù)提交到 ForkJoinPool。你可以提交兩種類型的任務(wù)。一種是沒(méi)有任何返回值的(一個(gè) “行動(dòng)”),另一種是有返回值的(一個(gè)"任務(wù)")。這兩種類型分別由 RecursiveAction 和 RecursiveTask 表示。接下來(lái)介紹如何使用這兩種類型的任務(wù),以及如何對(duì)它們進(jìn)行提交。

RecursiveAction

RecursiveAction 是一種沒(méi)有任何返回值的任務(wù)。它只是做一些工作,比如寫數(shù)據(jù)到磁盤,然后就退出了。一個(gè) RecursiveAction 可以把自己的工作分割成更小的幾塊,這樣它們可以由獨(dú)立的線程或者 CPU 執(zhí)行。

你可以通過(guò)繼承來(lái)實(shí)現(xiàn)一個(gè) RecursiveAction。示例如下:

package com;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

public class MyRecursiveAction extends RecursiveAction {

    private long workLoad = 0;

    public MyRecursiveAction(long workLoad) {
        this.workLoad = workLoad;
    }

    @Override
    protected void compute() {

        //如果工作超出閾值,將任務(wù)分解成更小的任務(wù)
        if(this.workLoad > 10) {
            System.out.println("將工作負(fù)載 : " + this.workLoad);
            //將工作負(fù)載分成多個(gè)子任務(wù)
            List<MyRecursiveAction> subtasks =
                    new ArrayList<MyRecursiveAction>();
            subtasks.addAll(createSubtasks());
            //將子任務(wù)加入到任務(wù)隊(duì)列中
            for(RecursiveAction subtask : subtasks){
                subtask.fork();
            }
        } else {
            System.out.println("自己完成工作量: " + this.workLoad);
        }
    }
    //將工作負(fù)載分成多個(gè)子任務(wù)
    private List<MyRecursiveAction> createSubtasks() {
        List<MyRecursiveAction> subtasks = new ArrayList<MyRecursiveAction>();
        //將工作負(fù)載分成兩個(gè)子任務(wù)   24/2=12  12/2=6
        MyRecursiveAction subtask1 = new MyRecursiveAction(this.workLoad / 2);
        MyRecursiveAction subtask2 = new MyRecursiveAction(this.workLoad / 2);
        subtasks.add(subtask1);
        subtasks.add(subtask2);

        return subtasks;
    }

    public static void main(String[] args) {
        ForkJoinPool forkJoinPool = new ForkJoinPool(4);
        MyRecursiveAction myRecursiveAction = new MyRecursiveAction(24);
        forkJoinPool.invoke(myRecursiveAction);

    }

}

RecursiveTask

RecursiveTask 是一種會(huì)返回結(jié)果的任務(wù)。它可以將自己的工作分割為若干更小任務(wù),并將這些子任務(wù)的執(zhí)行結(jié)果合并到一個(gè)集體結(jié)果。用法和RecursiveAction一樣唯一不同的就是可以返回值

以下是一個(gè) RecursiveTask 示例:

package com;

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

//配置RecursiveTask,返回值為L(zhǎng)ong
public class MyRecursiveTask  extends RecursiveTask<Long> {

    private long workLoad = 0;

    public MyRecursiveTask (long workLoad) {
        this.workLoad = workLoad;
    }

    @Override
    protected Long compute() {

        //如果工作超出閾值,將任務(wù)分解成更小的任務(wù)
        if(this.workLoad > 10) {
            System.out.println("將工作負(fù)載 : " + this.workLoad);
            //將工作負(fù)載分成多個(gè)子任務(wù)
            List<MyRecursiveTask > subtasks = new ArrayList<MyRecursiveTask >();
            subtasks.addAll(createSubtasks());
            //將子任務(wù)加入到任務(wù)隊(duì)列中
            for(RecursiveTask subtask : subtasks){
                subtask.fork();
            }
            //等待子任務(wù)執(zhí)行完,并得到其結(jié)果,并將結(jié)果相加
            long result = 0;
            for(MyRecursiveTask subtask : subtasks) {
                result += subtask.join();
            }
            return result;
        } else {
            System.out.println("自己完成工作量: " + this.workLoad);
            return 1L ;//返回計(jì)算結(jié)果
        }
    }
    //將工作負(fù)載分成多個(gè)子任務(wù)
    private List<MyRecursiveTask > createSubtasks() {
        List<MyRecursiveTask > subtasks = new ArrayList<MyRecursiveTask >();
        //將工作負(fù)載分成兩個(gè)子任務(wù)   24/2=12  12/2=6
        MyRecursiveTask  subtask1 = new MyRecursiveTask (this.workLoad / 2);
        MyRecursiveTask  subtask2 = new MyRecursiveTask (this.workLoad / 2);
        subtasks.add(subtask1);
        subtasks.add(subtask2);
        return subtasks;
    }

    public static void main(String[] args) {
        ForkJoinPool forkJoinPool = new ForkJoinPool(4);
        MyRecursiveTask myRecursiveAction = new MyRecursiveTask(24);
        Long invoke = forkJoinPool.invoke(myRecursiveAction);
        System.out.println("最終結(jié)果: " + invoke);//4 從結(jié)果可以看出,任務(wù)被分成了4個(gè)子任務(wù),每個(gè)子任務(wù)都是一個(gè)線程

    }

}

MyRecursiveTask 類繼承自 RecursiveTask,這也就意味著它將返回一個(gè) Long 類型的結(jié)果。MyRecursiveTask 示例也會(huì)將工作分割為子任務(wù),并通過(guò) fork() 方法對(duì)這些子任務(wù)計(jì)劃執(zhí)行。此外,本示例還通過(guò)調(diào)用每個(gè)子任務(wù)的 join() 方法收集它們返回的結(jié)果。子任務(wù)的結(jié)果隨后被合并到一個(gè)更大的結(jié)果,并最終將其返回。對(duì)于不同級(jí)別的遞歸,這種子任務(wù)的結(jié)果合并可能會(huì)發(fā)生遞歸。

Fork/Join 案例Demo

需求:使用 Fork/Join 計(jì)算 1-10000的和,當(dāng)一個(gè)任務(wù)的計(jì)算數(shù)量大于3000時(shí)拆分任務(wù),數(shù)量小于3000時(shí)計(jì)算。

因?yàn)?code>1~10000求和,耗時(shí)較少。下面我們將數(shù)據(jù)調(diào)大,求和1 ~ 59999999999(599億),然后來(lái)對(duì)比一下使用 Fork/Join求和 和 普通求和之間的效率差異。

普通求和

    public static void main(String[] args) {
        //開(kāi)始時(shí)間
        Long start = System.currentTimeMillis();
        long sum = 0l;
        for (long i = 1; i <= 59999999999L; i++) {
            sum+=i;
        }
        System.out.println(sum); //結(jié)果為負(fù)數(shù),因?yàn)槌隽薼ong的最大值了   ,平均消耗時(shí)間:16秒
        //結(jié)束時(shí)間
        Long end = System.currentTimeMillis();
        System.out.println("消耗時(shí)間:"+(end-start));
    }

Fork/Join求和

package com;

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

//配置RecursiveTask,返回值為L(zhǎng)ong
public class SumRecursiveTask   extends RecursiveTask<Long> {

    //大于3000要拆分(創(chuàng)建一個(gè)變量)
    //是否要拆分的臨界值
    private static final long THRESHOLD = 3000L;

    //起始值
    private final long start;
    //結(jié)束值
    private final long end;

    //構(gòu)造方法(傳遞起始值、結(jié)束值)
    public SumRecursiveTask(long start, long end) {
        this.start = start;
        this.end = end;
    }

    //任務(wù)編寫完成
    @Override
    protected Long compute() {
        long length = end - start;
        //計(jì)算
        if(length < THRESHOLD){
            long sum = 0;
            for (long i = start; i <= end; i++) {
                sum +=i;
            }
            return sum;
        }else{
            //拆分
            long middle = (start + end) /2;
            SumRecursiveTask left = new SumRecursiveTask(start,middle);//從小到大
            left.fork();
            SumRecursiveTask right = new SumRecursiveTask(middle+1,end);//從大到小
            right.fork();
            return left.join() +right.join();
        }
    }

    public static void main(String[] args) {
        Long start = System.currentTimeMillis();
        //放入線程池
        ForkJoinPool pool = new ForkJoinPool();
        SumRecursiveTask task = new SumRecursiveTask(1, 59999999999L);
        Long result = pool.invoke(task);
        System.out.println("result="+result); //結(jié)果為負(fù)數(shù),因?yàn)槌隽薼ong的最大值了   ,平均消耗時(shí)間:4秒
        Long end = System.currentTimeMillis();
        System.out.println("消耗時(shí)間:"+(end-start));
    }

}

總結(jié): 可以發(fā)現(xiàn)使用工作竊取算法能大大的提高我們計(jì)算的速度,理論上只要你電腦足夠快這個(gè)提升是沒(méi)有上限的 ,前提是任務(wù)是可拆分的

到此這篇關(guān)于Java線程池ForkJoinPool(工作竊取算法)的使用的文章就介紹到這了,更多相關(guān)Java線程池ForkJoinPool內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一文掌握maven??filtering標(biāo)簽

    一文掌握maven??filtering標(biāo)簽

    這篇文章主要介紹了maven??filtering標(biāo)簽,本文通過(guò)三種方法給大家講解maven?filtering標(biāo)簽,結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-02-02
  • Java工程中使用Mybatis (工程結(jié)合Mybatis,數(shù)據(jù)結(jié)合Swing使用))

    Java工程中使用Mybatis (工程結(jié)合Mybatis,數(shù)據(jù)結(jié)合Swing使用))

    這篇文章主要介紹了Java工程中使用Mybatis (工程結(jié)合Mybatis,數(shù)據(jù)可以結(jié)合Swing使用),需要的朋友可以參考下
    2017-04-04
  • java整合onlyoffice的各種踩坑記錄

    java整合onlyoffice的各種踩坑記錄

    這篇文章主要給大家介紹了關(guān)于java整合onlyoffice的各種踩坑,OnlyOffice是一種強(qiáng)大的在線協(xié)作軟件,專為企業(yè)和個(gè)人設(shè)計(jì),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • springboot 多數(shù)據(jù)源配置不生效遇到的坑及解決

    springboot 多數(shù)據(jù)源配置不生效遇到的坑及解決

    這篇文章主要介紹了springboot 多數(shù)據(jù)源配置不生效遇到的坑及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Spring MVC 擴(kuò)展和 SSM 框架整合步驟詳解

    Spring MVC 擴(kuò)展和 SSM 框架整合步驟詳解

    在前端頁(yè)面后后臺(tái)交互的過(guò)程中,需要一種格式清晰、高效且兩端都可以輕松使用的數(shù)據(jù)格式做交互的媒介,JSON正可以滿足這一需求,下面學(xué)習(xí)使用Spring MVC 框架處理JSON數(shù)據(jù),感興趣的朋友一起看看吧
    2024-08-08
  • java利用pdfbox+poi往pdf插入數(shù)據(jù)

    java利用pdfbox+poi往pdf插入數(shù)據(jù)

    這篇文章主要給大家介紹了關(guān)于java利用pdfbox+poi如何往pdf插入數(shù)據(jù)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-02-02
  • 全面解析java中的hashtable

    全面解析java中的hashtable

    以下是對(duì)java中的hashtable進(jìn)行了詳細(xì)的分析介紹。需要的朋友可以過(guò)來(lái)參考下
    2013-08-08
  • Java中Integer和int的區(qū)別解讀

    Java中Integer和int的區(qū)別解讀

    這篇文章主要介紹了Java中Integer和int的區(qū)別解讀,大家都知道他可以表示一個(gè)整數(shù),而且也知道可以表示整數(shù)的還有int,只是使用Integer的次數(shù)要比int多得多,今天我們就來(lái)好好探究一下Integer與int的區(qū)別以及更深處的知識(shí),需要的朋友可以參考下
    2023-12-12
  • 解決springboot mapper注入報(bào)紅問(wèn)題

    解決springboot mapper注入報(bào)紅問(wèn)題

    這篇文章主要介紹了解決springboot mapper注入報(bào)紅問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 一文帶你學(xué)習(xí)Java多維數(shù)組的使用技巧

    一文帶你學(xué)習(xí)Java多維數(shù)組的使用技巧

    Java作為一門廣泛應(yīng)用于各行各業(yè)的開(kāi)發(fā)語(yǔ)言,具有豐富的數(shù)據(jù)類型支持,其中多維數(shù)組是其重要的一種,多維數(shù)組可以更加方便地組織數(shù)據(jù),提高Java應(yīng)用程序的效率,本文將為大家介紹Java中多維數(shù)組的基本概念和常用操作,助力讀者更好地掌握多維數(shù)組的使用技巧
    2023-11-11

最新評(píng)論