Vaadin多线程应用中任务完成Notification无法即时显示问题
问题根因
这个异常是Vaadin异步UI更新的典型问题,核心原因有两点:
- Vaadin默认采用请求-响应的同步通信模式:服务端的UI变更只会在处理浏览器发起的请求时,随响应返回给前端渲染。你在自定义工作线程里调用
notification.open()时,没有浏览器发起的活跃请求,也没有主动触发服务端推送,所以这个UI变更会一直缓存在服务端,直到下一次用户点击按钮发起新请求,才会随本次响应一起发到前端渲染,就出现了“必须二次点击才弹出完成通知”的现象。 - 你当前手动设置ThreadLocal、手动加Session锁的写法不符合Vaadin异步更新的规范,既没有触发推送逻辑,还存在锁泄漏、上下文绑定不全的风险。
修复方案
1. 开启Vaadin异步推送能力
在你的路由视图类(或全局配置类)上添加@Push注解,开启服务端主动推送能力,配置为自动推送模式即可:
import com.vaadin.flow.component.page.Push; import com.vaadin.flow.router.Route; import com.vaadin.flow.shared.communication.PushMode; @Push(PushMode.AUTOMATIC) @Route("") public class MainView extends VerticalLayout { // 原有视图代码不变 }
如果是Spring Boot项目,也可以在实现了AppShellConfigurator的全局配置类上加这个注解,对所有视图生效。
2. 用UI.access()方法包裹后台线程中的UI操作
注意:你在
getIpButton()方法初始化时提前缓存UI实例的写法是正确的,避免了后台线程无法获取当前请求绑定UI的问题,这部分代码不需要修改。
删除你代码里手动调用UI.setCurrent()、VaadinSession.setCurrent()、手动加锁/解锁的逻辑,所有涉及UI修改的代码,都放到ui.access()的回调中执行。这个方法是Vaadin官方提供的异步UI更新工具,会自动完成上下文绑定、Session加锁、执行完成后触发推送、异常场景自动释放锁的全流程,避免手动操作的疏漏。
修改后的按钮任务提交逻辑如下:
ipButton.addClickListener(_e -> { int orderThread = orderIndex.getAndIncrement(); openBeginNotification(orderThread); executor.submit(() -> { try { long sleepTime = (long) (Math.random() * (10 - 5) + 5); System.out.printf("%d: %ds\n", orderThread, sleepTime); Thread.sleep(sleepTime * 1000); IpDTO ip = restTemplate.getForObject("http://ip.jsontest.com/", IpDTO.class); System.out.printf("%d: %s\n", orderThread, ip); // 安全执行UI更新并自动推送 ui.access(() -> getFinishNotification(orderThread).open()); } catch (InterruptedException e) { throw new RuntimeException(e); } }); });
可选优化
你当前设置的Notification显示时长为1000毫秒(1秒),测试时可以适当调大这个值,避免通知弹出后过快消失,不方便验证效果。
内容的提问来源于stack exchange,提问作者Adam Barča
相关产品推荐
相关产品推荐

