簡體   English   中英

fork-join有工作要做時沒有工作線程?

[英]No worker threads when fork-join has work to do?

我有一個錯誤,該錯誤現在在生產中出現了兩次,該錯誤中有一個我的fork / join池停止工作,即使它有工作要做,並且正在添加更多工作。

到目前為止,這是我得出的結論,它解釋了為什么要執行的任務隊列已滿,而任務結果的流卻停止了。 我有線程轉儲,任務生產者線程正在其中等待派生/聯接提交完成,但是沒有ForkJoinPool工作線程對此做任何事情。

"calc-scheduling-pool-4-thread-2" #65 prio=5 os_prio=0  tid=0x00000000102e39f0 nid=0x794a in Object.wait() [0x00002ad900a06000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    at java.util.concurrent.ForkJoinTask.externalAwaitDone(ForkJoinTask.java:334)
    - locked <0x000000061ad08708> (a com.....Engine$Calculation)
    at java.util.concurrent.ForkJoinTask.doJoin(ForkJoinTask.java:391)
    at java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:719)
    at java.util.concurrent.ForkJoinPool.invoke(ForkJoinPool.java:2613)
    at com...Engine.calculateSinceLastBatch(Engine.java:141)

不管我在做什么,這都不應該對嗎? 線程轉儲是從檢測到初始條件后的多個小時開始的。 在運行時中,我還有另外兩個ForkJoinPools都在有許多輔助線程的情況下正常運行。

該池的並行度為1(我知道這很愚蠢,但不應破壞fork / join池的正確性)。 在我的任務隊列填滿並且線程轉儲沒有顯示任何工作線程之前,沒有檢測到任何錯誤或異常。

其他人看到了嗎? 我丟失了某些東西,或者fork / join中存在一個錯誤,但從未為我啟動工作線程。

運行時是Java 8

用代碼更新

這是我們在生產中使用fork / join的合理簡化。 我們有三個引擎,其中只有一個配置了1的並行度。

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.*;

public class Engine {

    BlockingQueue<Calculation> externalQueue = new LinkedBlockingQueue<>(100000);
    ScheduledExecutorService scheduling = Executors.newScheduledThreadPool(3);
    static ForkJoinPool forkJoin = new ForkJoinPool(1);

    public static void main(String[] args) {
        new Engine().start();
    }

    void start() {
        final AtomicInteger batch = new AtomicInteger(0);
        // data comes in from external systems
        scheduling.scheduleWithFixedDelay(
                () -> produceData(batch.getAndIncrement()),
                500,
                500,
                TimeUnit.MILLISECONDS);
        // internal scheduling processes data with a fixed delay
        scheduling.scheduleWithFixedDelay(
                this::calculate,
                1000,
                1000,
                TimeUnit.MILLISECONDS);
    }

    void produceData(final int batch) {
        System.out.println(Thread.currentThread().getName() + " => submitting data for batch " + batch);
        Stream<Integer> data = IntStream.range(0, 10).boxed();
        data.map((i) -> new Calculation(batch, i)).forEach(externalQueue::offer);
    }

    void calculate() {
        int available = externalQueue.size();
        List<Calculation> tasks = new ArrayList<>(available);
        externalQueue.drainTo(tasks);
        // invoke will block for the results to be calculated before continuing
        forkJoin.invoke(new CalculationTask(tasks, 0, tasks.size()));
        System.out.println("done with calculations at " + new Date());
    }

    static class CalculationTask extends RecursiveAction {

        static int MIN_CALCULATION_THRESHOLD = 3;

        List<Calculation> tasks;
        int start;
        int end;

        CalculationTask(List<Calculation> tasks, int start, int end) {
            this.tasks = tasks;
            this.start = start;
            this.end = end;
        }

        // if below a threshold, calculate here, else fork to new CalculationTasks
        @Override
        protected void compute() {
            int work = end - start;
            if (work <= threshold()) {
                for (int i = start; i < end; i++) {
                    Calculation calc = tasks.get(i);
                    calc.calculate();
                }
                return;
            }

            invokeNewActions();
        }

        int threshold() {
            return Math.max(tasks.size() / forkJoin.getParallelism() / 2, MIN_CALCULATION_THRESHOLD);
        }

        void invokeNewActions() {
            invokeAll(
                    new CalculationTask(tasks, start, middle()),
                    new CalculationTask(tasks, middle(), end));
        }

        int middle() {
            return (start + end) / 2;
        }
    }

    static class Calculation {

        int batch;
        int data;

        Calculation(int batch, int data) {
            this.batch = batch;
            this.data = data;
        }

        void calculate() {
            // does some work and pushes results to a listener
            System.out.println(Thread.currentThread().getName() + " => calculation complete on batch " + batch
                            + " for " + data);
        }
    }

}

等待在java.util.concurrent.ForkJoinTask.externalAwaitDone(ForkJoinTask.java:334)

這告訴我F / J可能正在使用您的提交線程作為工作線程。 請遵循invokeAll中的代碼。 在任務提交執行后,代碼需要Future,並且最終以((ForkJoinTask)futures.get(i))。quietlyJoin();結束。 悄悄加入去做加入。

如果池將您的提交線程用作工作線程,則如果(ForkJoinWorkerThread)的(Thread.currentThread())實例不為true,則它將終止於externalAwaitDone()中。

問題可能是您的提交線程不是真正的工作者,因此它永遠不會喚醒。 使用提交線程作為工作程序存在很多問題,這可能是另一個問題。

正如@ John-Vint所說,未經測試,這個答案只是一個猜測。 為什么不將並行度設置為> 1並完成它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM