gpt4 book ai didi

java - fork-join 有工作要做时没有工作线程?

转载 作者:行者123 更新时间:2023-12-03 13:09:23 25 4
gpt4 key购买 nike

我有一个错误现在在生产中出现了两次,我的一个 fork/join 池停止工作,即使它有工作要做并且正在添加更多工作。

这是我迄今为止得出的结论,用于解释为什么要做的任务队列正在填满,而任务结果流正在停止。我有线程转储,我的任务生产者线程正在等待 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();quietlyJoin 转到 doJoin。

在那里,如果 (Thread.currentThread()) instanceof ForkJoinWorkerThread) 如果池将您的提交线程用作工作线程,则它不会为真,它最终会进入 externalAwaitDone()。

问题可能是您的提交线程永远不会唤醒,因为它不是真正的工作线程。使用提交线程作为 worker 存在很多问题,这可能是另一个问题。

正如@John-Vint 所说,未经测试,这个答案只是一个猜测。为什么不将并行度设置为 >1 并完成它。

关于java - fork-join 有工作要做时没有工作线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40431603/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com