Docs

Documentation versions (currently viewingVaadin 24)
Documentation translations (currently viewingChinese)

此页面是由官方文档(http://vaadin.com/docs)机器翻译而来。内容可能存在错误、不准确或表述不当之处。Vaadin 对本翻译的准确性、可靠性或及时性不作任何保证或陈述。

用户界面线程 Flow

如何在 Vaadin Flow 用户界面中使用线程。

开发人员经常使用服务器推送来从后台任务更新用户界面(参见Background Jobs - UI Interaction)。但在 Vaadin Flow 中,有时也需要为用户界面本身启动一个单独的线程。例如,您可能希望实时显示服务器的日期和时间。

如果您熟悉 Swing,可能会倾向于使用 Timer,或者手动启动一个新 Thread。在 Flow 中,这并不是一个好的方法。Flow 应用是多用户应用,可能存在成千上万个并发用户。如果每个用户都创建自己的 Timer 或启动自己的 Thread,线程可能会耗尽。如果发生这种情况,应用会崩溃。

更好的方案是使用虚拟线程,或使用 Spring 的 TaskExecutorTaskScheduler。以下各节将介绍这些内容,并提供一些示例。

Note
本页的示例仅在启用了服务器推送的情况下有效。关于如何启用服务器推送的信息,请参阅服务器推送 文档页面。

虚拟线程

如果您使用支持虚拟线程的 Java 版本,则可以在需要时启动新的虚拟线程。

以下是一个按钮点击监听器的示例,用于启动一个新的虚拟线程:

Source code
Java
button.addClickListener(clickEvent -> {
    Thread.ofVirtual().start(UI.getCurrent().accessLater(() -> {
        // 在此处执行 UI 操作。
    }, null));
});

这是启动新用户界面线程的最简单方式。如果您能够使用虚拟线程,应将其作为首选方案。但如果您遇到问题,请改用 TaskExecutor

对于计划执行的任务,您仍应使用 TaskScheduler。这将在本页面稍后的内容中介绍。

Task Executor

您还可以使用 Spring 的 TaskExecutorTaskScheduler 来直接从用户界面启动任务。有关它们的配置请参阅 Background Jobs 文档页面。

在使用 TaskExecutorTaskScheduler 启动任务之前,应确保这些任务确实与 UI 操作相关,而非后台任务。要正确使用它们,应当将它们注入到您的视图中,并在需要时调用。

以下示例展示了一个将 TaskExecutor 作为构造函数参数注入的视图:

Source code
Java
@Route
public class MyView extends VerticalLayout {

    private final TaskExecutor taskExecutor;

    public MyView(TaskExecutor taskExecutor) {
        this.taskExecutor = taskExecutor;
        ...
    }
}

此示例通过按钮点击监听器在后台线程中启动了一个 UI 操作:

Source code
Java
button.addClickListener(clickEvent -> {
    taskExecutor.execute(UI.getCurrent().accessLater(() -> {
        // 在此处执行 UI 操作。
    }, null));
});

由于调用了 UI.accessLater(),在任务完成后,用户界面会通过服务器推送进行更新。

Caution
不要在 Flow 的视图中使用 @Async 注解。这会导致视图变成代理类,而代理类在 Vaadin 中无法正常工作。

Task Scheduler

您可以使用 TaskScheduler 来调度任务。在使用时,您需要在组件附着(attached)时安排任务,并在组件分离(detached)时取消它。

下面的示例将每秒执行一次任务。任务将 currentTimeLabel 的文本设置为服务器当前的日期和时间。当组件被分离时,任务将被取消:

Source code
Java
@Override
protected void onAttach(AttachEvent attachEvent) {
    var task = taskScheduler.scheduleAtFixedRate(
        attachEvent.getUI().accessLater(() -> {
            currentTimeLabel.setText(Instant.now().toString());
        }, null), Duration.ofSeconds(1)
    );
    addDetachListener(detachEvent -> {
        detachEvent.unregisterListener();
        task.cancel(true);
    });
}

您在 Task Scheduler 中执行的任务应当执行迅速。如果您需要计划执行耗时较长的任务,应将其交给 TaskExecutor 来执行。

Caution
切勿在 Flow 的视图中使用 @Scheduled 注解。这会导致视图变成代理类,而代理类在 Vaadin 中无法正常工作。