gpt4 book ai didi

java - 在后台线程中运行的可编辑任务队列

转载 作者:行者123 更新时间:2023-11-30 07:54:08 25 4
gpt4 key购买 nike

我知道这个问题已经被回答过很多次了,但我很难理解它是如何工作的。

因此,在我的应用程序中,用户必须能够选择将添加到队列中的项目(使用 ListView 显示在 ObservableList<Task> 中),并且每个项目都需要由 ExecutorService 按顺序处理。 。

此外,该队列应该是可编辑的(更改顺序并从列表中删除项目)。

private void handleItemClicked(MouseEvent event) {
if (event.getClickCount() == 2) {
File item = listView.getSelectionModel().getSelectedItem();
Task<Void> task = createTask(item);
facade.getTaskQueueList().add(task); // this list is bound to a ListView, where it can be edited
Future result = executor.submit(task);
// where executor is an ExecutorService of which type?

try {
result.get();
} catch (Exception e) {
// ...
}
}
}

尝试过 executor = Executors.newFixedThreadPool(1)但我无法控制队列。
我读到ThreadPoolExecutor和队列,但我很难理解它,因为我对并发还很陌生。

我需要运行该方法 handleItemClicked在后台线程中,这样 UI 就不会卡住,我怎样才能做到最好?

总结:如何实现可编辑并由后台线程顺序处理的任务队列?

请帮我解答一下

编辑使用SerialTaskQueue vanOekel 的类(class)帮助了我,现在我想将任务列表绑定(bind)到我的 ListView .

ListProperty<Runnable> listProperty = new SimpleListProperty<>();
listProperty.set(taskQueue.getTaskList()); // getTaskList() returns the LinkedList from SerialTaskQueue
queueListView.itemsProperty().bind(listProperty);

显然这不起作用,因为它需要一个 ObservableList。有一种优雅的方法吗?

最佳答案

我能想到的最简单的解决方案是在执行程序之外维护任务列表,并使用回调向执行程序提供下一个任务(如果可用)。不幸的是,它涉及任务列表上的同步和指示任务正在执行的 AtomicBoolean

回调只是一个Runnable,它包装要运行的原始任务,然后“回调”以查看是否有另一个任务要执行,如果有,则使用(后台)执行它执行者。

需要同步来保持任务列表有序并处于已知状态。任务列表可以同时由两个线程修改:通过在执行程序(后台)线程中运行的回调,以及通过 UI 前台线程执行的 handleItemClicked 方法。这反过来意味着永远无法确切知道任务列表何时为空。为了保持任务列表有序并处于已知的固定状态,需要任务列表的同步。

这仍然留下了一个模糊的时刻来决定任务何时准备好执行。这就是 AtomicBoolean 发挥作用的地方:值集总是立即可用并由任何其他线程读取,并且 compareAndSet 方法将始终确保只有一个线程获得“OK” .

将同步和 AtomicBoolean 的使用结合起来,可以创建一个具有“关键部分”的方法,该方法可以由前台线程和后台线程同时调用,以触发如果可能的话执行新任务。下面的代码的设计和设置方式使得这样的方法 (runNextTask) 可以存在。好的做法是使并发代码中的“关键部分”尽可能简单和明确(这通常会导致高效的“关键部分”)。

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

public class SerialTaskQueue {

public static void main(String[] args) {

ExecutorService executor = Executors.newSingleThreadExecutor();
// all operations on this list must be synchronized on the list itself.
SerialTaskQueue tq = new SerialTaskQueue(executor);
try {
// test running the tasks one by one
tq.add(new SleepSome(10L));
Thread.sleep(5L);
tq.add(new SleepSome(20L));
tq.add(new SleepSome(30L));

Thread.sleep(100L);
System.out.println("Queue size: " + tq.size()); // should be empty
tq.add(new SleepSome(10L));

Thread.sleep(100L);
} catch (Exception e) {
e.printStackTrace();
} finally {
executor.shutdownNow();
}
}

// all lookups and modifications to the list must be synchronized on the list.
private final List<Runnable> tasks = new LinkedList<Runnable>();
// atomic boolean used to ensure only 1 task is executed at any given time
private final AtomicBoolean executeNextTask = new AtomicBoolean(true);
private final Executor executor;

public SerialTaskQueue(Executor executor) {
this.executor = executor;
}

public void add(Runnable task) {

synchronized(tasks) { tasks.add(task); }
runNextTask();
}

private void runNextTask() {
// critical section that ensures one task is executed.
synchronized(tasks) {
if (!tasks.isEmpty()
&& executeNextTask.compareAndSet(true, false)) {
executor.execute(wrapTask(tasks.remove(0)));
}
}
}

private CallbackTask wrapTask(Runnable task) {

return new CallbackTask(task, new Runnable() {
@Override public void run() {
if (!executeNextTask.compareAndSet(false, true)) {
System.out.println("ERROR: programming error, the callback should always run in execute state.");
}
runNextTask();
}
});
}

public int size() {
synchronized(tasks) { return tasks.size(); }
}

public Runnable get(int index) {
synchronized(tasks) { return tasks.get(index); }
}

public Runnable remove(int index) {
synchronized(tasks) { return tasks.remove(index); }
}

// general callback-task, see https://stackoverflow.com/a/826283/3080094
static class CallbackTask implements Runnable {

private final Runnable task, callback;

public CallbackTask(Runnable task, Runnable callback) {
this.task = task;
this.callback = callback;
}

@Override public void run() {
try {
task.run();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
callback.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

// task that just sleeps for a while
static class SleepSome implements Runnable {

static long startTime = System.currentTimeMillis();

private final long sleepTimeMs;
public SleepSome(long sleepTimeMs) {
this.sleepTimeMs = sleepTimeMs;
}
@Override public void run() {
try {
System.out.println(tdelta() + "Sleeping for " + sleepTimeMs + " ms.");
Thread.sleep(sleepTimeMs);
System.out.println(tdelta() + "Slept for " + sleepTimeMs + " ms.");
} catch (Exception e) {
e.printStackTrace();
}
}

private String tdelta() { return String.format("% 4d ", (System.currentTimeMillis() - startTime)); }
}
}

更新:如果需要串行执行任务组,请查看调整后的实现 here .

关于java - 在后台线程中运行的可编辑任务队列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32914420/

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