gpt4 book ai didi

java - 为什么来自 javaFX 的 ScheduledService 不能与作为任务的具体类一起使用?

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:31:28 25 4
gpt4 key购买 nike

当我注意到 ScheduledService 在运行几次后停止调度时,我正在处理我的 JavaFX 应用程序。

我找不到任何明显的原因。当我跟踪 ScheduledService 的状态时,它似乎切换到 SCHEDULED 状态,然后变得沉默。我将我的代码减少到几乎没有,希望能缩小问题的范围。我发现当我 ScheduledService 创建匿名类的任务时,问题不会发生,但是当我使用子类或顶级类时,它会发生。

package application;

import javafx.application.Application;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.stage.Stage;

public class MyApplication extends Application
{
private static volatile int counter;

@Override
public void start( Stage primaryStage )
{
ScheduledService<Void> svc = new ScheduledService<>()
{
@Override
protected Task<Void> createTask()
{
return new MyTask();// if i use new Task<Void>{...} it works fine
}
};
svc.start();
}

private static class MyTask extends Task<Void>
{
@Override
protected Void call() throws Exception
{
System.out.println( "MyTask#call() " + counter++ );
return null;
}
}

public static void main( String[] args )
{
launch( args );
}
}

输出

MyTask#call() 0
MyTask#call() 1
MyTask#call() 2
MyTask#call() 3
MyTask#call() 4
MyTask#call() 5
MyTask#call() 6
MyTask#call() 7
MyTask#call() 8
MyTask#call() 9
MyTask#call() 10
MyTask#call() 11
MyTask#call() 12
MyTask#call() 13
MyTask#call() 14

这是一个错误吗?

我正在使用

Windows 10

OracleOpen_jdk-12.0.1

javafx-sdk-12.0.1

最佳答案

我可以使用以下环境重现该问题:

  • Windows 10
  • OpenJDK 12.0.1
  • OpenJFX 12.0.1

稍微修改一下代码就可以看出问题所在:

import java.lang.ref.Cleaner;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Application;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.stage.Stage;

public class Main extends Application {

private static final Cleaner CLEANER = Cleaner.create();
private static final AtomicInteger COUNTER = new AtomicInteger();

@Override
public void start(Stage primaryStage) {
var service = new ScheduledService<Void>() {

@Override
protected Task<Void> createTask() {
return new MyTask();
}

};
CLEANER.register(service, () -> {
System.out.println("SERVICE GARBAGE COLLECTED!");
System.exit(0);
});
service.start();
}

private static class MyTask extends Task<Void> {

@Override
protected Void call() throws Exception{
System.out.println("MyTask#call() " + COUNTER.incrementAndGet());
return null;
}

}

}

以上输出:

MyTask#call() 1
MyTask#call() 2
MyTask#call() 3
MyTask#call() 4
MyTask#call() 5
MyTask#call() 6
MyTask#call() 7
MyTask#call() 8
MyTask#call() 9
MyTask#call() 10
MyTask#call() 11
MyTask#call() 12
MyTask#call() 13
MyTask#call() 14
MyTask#call() 15
MyTask#call() 16
MyTask#call() 17
MyTask#call() 18
MyTask#call() 19
SERVICE GARBAGE COLLECTED!

这就解释了正在发生的事情——ScheduledService实例正在被垃圾收集。这是有道理的,因为您没有保持对 ScheduledService 的强引用。实例,Task 也没有正在执行的实例。一旦ScheduledService实例已被垃圾收集,无法安排另一个 Task下一个执行周期。


I'm still a bit confused why it works with a anonymous class then. I mean in my example the class MyTask is static but even when it is not it does not work. Objects of a non-static inner class should have a reference to there outer class objects.

的确,非静态嵌套类或匿名类维护对封闭类实例的引用。但是,封闭类是 Main ( MyApplication 在你的代码中),而不是匿名的 ScheduledService类。


return new MyTask();// if i use new Task<Void>{...} it works fine

使用 new Task<Void>() { ... } 的原因有效是因为现在封闭的实例是 ScheduledService实例。

ScheduledService类使用内部 java.util.Timer用于调度 Task 的实例供以后执行。因此,在等待执行 Task 时由 Timer 使用的线程强烈可达.在执行时,Task可由执行它的线程强访问(该线程来自 ExecutorScheduledService#executor 属性一起使用)。

所以,当使用匿名 Task由匿名 ScheduledService 括起来, ScheduledService最终是强可达的。

  • 调度/执行线程 → 任务 → ScheduledService

奇怪的是,如果我添加代码来显示主 Stage ScheduledService永远不会被垃圾收集。我不确定为什么会这样。即使我使用 Platform.setImplicitExit(false) 也是如此并关闭 Stage .

关于java - 为什么来自 javaFX 的 ScheduledService 不能与作为任务的具体类一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57437369/

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