0

我正在尝试实现一个线程安全的解决方案来记录已完成的成功任务的计数,这些任务最终将绑定到 UI 上显示的标签。但是,当我使用下面的 AtomicInteger 时,它会在任务开始运行时锁定我的 UI,但是,如果我删除所有 AtomicInteger 引用,一切正常。是否有一种非阻塞、线程安全的方式可以实现这一点?

public void handleSomeButtonClick(){
    if(!dataModel.getSomeList().isEmpty()) {
        boolean unlimited = false;
        int count = 0;
        AtomicInteger successCount = new AtomicInteger(0);

        if(countSelector.getValue().equalsIgnoreCase("Unlimited"))
            unlimited = true;
        else
            count = Integer.parseInt(countSelector.getValue());

        while(unlimited || successCount.get() < count) {
            Task task = getSomeTask();
            taskExecutor.submit(task);
            task.setOnSucceeded(event -> {
                if (task.getValue())
                    log.info("Successfully Completed Task | Total Count: " + successCount.incrementAndGet());
                else
                    log.error("Failed task");
            });
        }
    }
}
4

3 回答 3

1

您的循环等待完成一定数量的任务。它甚至可能是一个无限循环。

这不是一个好主意:

  • 您阻止了似乎是 JavaFX 应用程序线程的调用线程。
  • 您无法控制提交的任务数量。count可能是 3,但由于您只在循环中安排任务,因此可以在第一个任务完成之前创建和安排 1000 个或更多任务。

此外,如果您使用onSucceeded/ onFailed,则不需要使用AtomicInteger或任何类似的同步,因为这些处理程序都在 JavaFX 应用程序线程上运行。

您的代码可以这样重写:

private int successCount;

private void scheduleTask(final boolean unlimited) {
    Task task = getSomeTask();
    task.setOnSucceeded(event -> {
        // cannot get a Boolean from a raw task, so I assume the task is successfull iff no exception happens
        successCount++;
        log.info("Successfully Completed Task | Total Count: " + successCount);
        if (unlimited) {
            // submit new task, if the number of tasks is unlimited
            scheduleTask(true);
        }
    });
    // submit new task on failure
    task.setOnFailed(evt -> scheduleTask(unlimited));
    taskExecutor.submit(task);
}

public void handleSomeButtonClick() {
    if(!dataModel.getSomeList().isEmpty()) {
        successCount = 0;
        final boolean unlimited;
        final int count;

        if(countSelector.getValue().equalsIgnoreCase("Unlimited")) {
            unlimited = true;
            count = 4; // set limit of number of tasks submitted to the executor at the same time
        } else {
            count = Integer.parseInt(countSelector.getValue());
            unlimited = false;
        }

        for (int i = 0; i < count; i++) {
            scheduleTask(unlimited);
        }
    }
}

注意:此代码存在handleButtonClick在之前的任务完成之前被多次单击的风险。您应该阻止在旧任务完成之前安排新任务,或者使用包含一个int而不是计数的某些引用类型,在其中创建此对象handleSomeButtonClick并将此对象传递给scheduleTask.

于 2018-11-30T09:30:28.490 回答
0

您的 UI 锁定意味着您在 FX 应用程序线程中进行计数(successCount.get() < count)。我不明白你为什么一直在while循环中提交任务,你想做哪一个?(1) 启动 X(eg 10) 个任务并计算有多少任务是成功的。或(2)继续开始新任务并看到计数上升。

if(2) 然后在后台线程中运行整个 while 循环,在 Platform->runlater() 中更新 UI。

if(1) 使用 Future / CompletableFuture,或更强大的版本 Future 在 3rd 方包中,如 vavr。

于 2018-11-30T08:31:20.743 回答
0

您的问题是 future.get() 阻塞并等待结果。如果您使用 Vavr 库,这将很简单。因为它可以将代码附加到它的未来,当成功或失败时自动运行。所以你不必等待。这是一个使用 Vavr 未来的例子。

        CheckedFunction0<String> thisIsATask = () -> {
        if ( /*do something*/ ){
            throw new Exception("Hey");
        }
        return "ABC";
    };

    List<Future<String>> futureList = new ArrayList<>();

    for (int x = 0; x < 10; x++) {
        futureList.add(Future.of(getExecutorService(), thisIsATask));
    }

    futureList.forEach((task) -> {
        // This will run if success
        task.onSuccess(s -> s.equals("ABC") ? Platform.runLater(()->UpdateCounter()) : wtf());
        // Your get the exception if it is fail;
        task.onFailure(e -> e.printStackTrace());
        // task.onComplete() will run on any case when complete
    });

这不是阻塞,onSucess onFailure 或 onComplete 处的代码将在任务完成或捕获异常时运行。

注意:Future.of 将使用您传入的 executorService 在新线程上运行每个任务,一旦任务完成,您在 onSuccess 提供的代码将继续在该线程上运行,因此如果您调用 javafx,请记住 Platform.runLater()

另外,如果您想在所有任务完成后运行某些东西,那么

    // the code at onComplete will run when tasks all done
    Future<Seq<String>> all = Future.sequence(futureList);
    all.onComplete((i) -> this.btnXYZ.setDisable(false));
于 2018-11-30T16:56:13.657 回答