Java - 执行后计时器未被移除

10

我有一个应用程序,它会在用户操作时启动计时器来显示一条消息。在JDK分析器中,似乎所有其他线程在执行后都被垃圾收集器(我猜是)删除了,但是创建的计时器没有被删除。可能会发生什么?

我的计时器:

/**
 * @param owner
 * @param added
 */
public static void splashParentWithAnimation(AnchorPane owner, Parent added,double posX,double posY) {
    // addParentWithAnimation(owner, added);
    owner.getChildren().add(added);

    AnchorPane.setLeftAnchor(added, posX);

    AnchorPane.setTopAnchor(added,  posY);

    FadeTransition ft1 = new FadeTransition(Duration.millis(300), added);
    ft1.setFromValue(0.0);
    ft1.setToValue(1.0);
    ft1.play();


    Timer messagePrinter = new Timer();
    messagePrinter.schedule(new TimerTask() {

        @Override
        public void run() {
            Platform.runLater(() -> {

                if (!owner.getChildren().contains(added))
                    return;

                FadeTransition ft1 = new FadeTransition(Duration.millis(300), added);
                ft1.setFromValue(1.0);
                ft1.setToValue(0.0);
                ft1.play();
                ft1.setOnFinished((e) -> {

                    if (owner.getChildren().contains(added))
                        owner.getChildren().remove(added);
                });

            });

        }
    },  1000);
}

JDK分析器: enter image description here

这是因为我正在使用静态方法吗?还是需要自己销毁它?

3个回答

8
实际上,这里没有关于计时器终止的问题。您在分析器中看到的线程已经终止 - 它们左侧有一个白色框,表示它们已经停止运行。
分析器显示了程序执行期间创建的所有线程,即使这些线程已经停止并被垃圾回收。
您可以通过以下方式轻松确认:不使用lambda,而是创建TimerTask的子类来执行相同的任务,并重新定义其finalize()方法以打印一些内容。当进行垃圾回收时,您将看到任务正在完成。仅当线程停止运行时才会发生这种情况,因为这是Thread类中唯一放弃对其RunnableTimerTask实现)引用的位置。
另一种确认方法就是从表格顶部的“View”下拉列表中选择“Live Threads”。
此外,我建议您将Timer替换为更好的工具。每次需要延迟一些任务时创建线程太浪费资源。可以尝试使用ScheduledThreadPoolExecutor,它似乎更适合您的任务:
// Create a shared executor with a single thread
private final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);

// Instead of creating a Timer, schedule the task
executor.schedule(() -> {
    // Do what you need here
}, 1, TimeUnit.SECONDS);

// Don't forget to terminate the scheduler when you don't need it anymore
scheduler.terminate();

如果您有太多的计划任务,并且这些任务不够小,可以将多个线程添加到执行器中。


4

这是因为您需要手动处理计时器。

如果您使用java.util.Timer,则需要调用cancel方法以释放资源。


name_of_timer.cancel(); ? - Antonios Tsimourtos
是的。在这种情况下,messagePrinter.cancel(); - talex
它会取消计时器,但线程仍然卡在池中。 - Abdullah Asendar
也许你有其他地方使用了 Timer - talex
@talex 我非常确定,这个方法是抽象的会对此有任何影响吗? - Abdullah Asendar
1
我还有另一个选项供您选择:您可以在调用之间共享同一个“计时器”。只需将其移动到静态字段即可。这样它将仅创建一个线程,您无需取消它。 - talex

1

您的计时器是使用非守护线程创建的,非守护线程会阻塞程序的终止。您应该使用使用守护线程创建计时器的构造函数。

boolean daemon=true; Timer messagePrinter = new Timer(daemon);

但我建议像Andrew Lygin建议的那样使用ExecutorService。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接