JavaFx新线程与输入相同的任务

时间:2018-03-14 10:16:39

标签: java json multithreading javafx task

我正在研究JavaFX桌面应用程序,我有一个按钮,应从嵌入式设备的内存中读取并将其打印到JSON中。我已经实现了一个执行该操作的Task,并且此Task作为参数传递给按钮事件处理程序中的新线程。问题是,这只能工作一次。之后,即使在按钮单击时生成新线程,也不会再次调用Task的call()方法。这是代码:

任务定义:

42
46

线程创建:

Task readValDaemon = new Task<Void>() {
    @Override
    public Void call() {
        //This functions reads from memory and writes the JSON
        readDataHI(connection,commandListHI,statusHI);
        return null;
    }
};

2 个答案:

答案 0 :(得分:1)

正如在其他答案中所观察到的,TaskFutureTask的实现。来自Task文档:

  

FutureTask一样,Task是一次性类,无法重复使用。有关可重复使用的Service,请参阅Worker

因此您无法重复使用任务。第二次和后续尝试运行它将会无声地失败。

您每次都可以直接创建新任务:

private Task<Void> createReadValTask() {
    return new Task<Void>() { 
        @Override
        public Void call() {
            //This functions reads from memory and writes the JSON
            readDataHI(connection,commandListHI,statusHI);
            return null;
        }
    };
}

然后再做

readData.setOnMouseClicked(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent event) {

        Thread readValThread = new Thread(createReadValTask());
        readValThread.setDaemon(true);
        readValThread.start();
    }
});

您还可以考虑使用专为重复使用而设计的Service。它基本上封装了“每次创建一个新任务”功能,但增加了许多有用的UI回调。 Service还为您管理线程池(通过Executor),因此您不再需要担心您可能创建了太多线程。 (如果你想控制它,也可以指定Executor。)

所以,例如:

Service<Void> readValDaemon = new Service<Void>() {
    @Override
    protected Task<Void> createTask() {
        return new Task<Void>() {
            @Override
            public Void call() {
                //This functions reads from memory and writes the JSON
                readDataHI(connection,commandListHI,statusHI);
                return null;
            }
        };
    }
};

然后

readData.setOnMouseClicked(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent event) {
        readValThread.restart();
    }
});

如果在服务已经运行时单击鼠标,则会自动取消已在运行的任务,并重新启动新任务。如果需要,您可以添加支票,或者将disable状态readData绑定到Service的状态,如果您愿意的话。

答案 1 :(得分:0)

Task是一种错误的工具。它非常有目的地只设计为运行一次,因为它是future的一种。它将结果(在您的情况下为null)存储为一种memoization,以避免执行比必要更多次的昂贵操作。所以Task最适合需要进行昂贵计算一次的情况,通常你会想要在某一点上得到它的结果。

documentation for Task非常彻底,所以我会给你一个阅读。

在您的情况下,只需使用普通Runnable。您可以使用lambda表达式:

readData.setOnMouseClicked(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent event)
    {
        Thread readValThread = new Thread(() -> readDataHI(a, b, c));
        readValThread.setDaemon(true);
        readValThread.start();
    }
});

另外,在现代Java中,手动创建线程并不算是非常好的做法。强烈考虑改为ExecutorService