首页 > 解决方案 > Vaadin 14 - 如何确保在 SessionDestroy 之后清理与会话相关的对象

问题描述

这是我在 Vaadin 14.1.18 SpringBoot 应用程序中的 MainView 类。

@Route
public class MainView extends VerticalLayout implements SessionDestroyListener{
    Logger logger = LoggerFactory.getLogger(MainView.class);

    public MainView() {
        // To make sure of doing some Houskeeping after Session Timeout
        VaadinService.getCurrent().addSessionDestroyListener(this);

        String sessionId = VaadinSession.getCurrent().getSession().getId();
        Zombie zombie = new Zombie(sessionId);
        zombie.start();

        add(new Span("Hey There! I'm at your disposal!!"));
    }

    @Override
    public void sessionDestroy(SessionDestroyEvent sessionDestroyEvent) {
        logger.warn("Just received a SessionDestroy Event with SessionId :: [{}]", sessionDestroyEvent.getSession().getSession().getId());
        // Performing the HouseKeeping stuff
    }
}

我还实现了一个僵尸进程,如下所示,我在我的 MainView 类中实例化了它。

public class Zombie extends Thread {
    Logger logger = LoggerFactory.getLogger(Zombie.class);
    private String sessionId;

    public Zombie(String sessionId) {
        this.sessionId = sessionId;
    }

    public void run() {
        while (true) {
            try {
                Thread.sleep(60000);
                Calendar cal1 = Calendar.getInstance();
                logger.info("Session [{}] :: Hey, I'm still alive at {}", sessionId, cal1.getTime());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

问题是,当相应的会话被破坏时,为什么附加到它的对象仍然存在?以下日志显示,即使在 SessionDestroy 之后,僵尸进程仍在运行。我是否必须采取一些具体措施来明确清理这些对象,或者 Vaadin 应该以某种方式自动处理它?

2020-03-07 02:32:02.891  INFO 6336 --- com.mypackage.Zombie : Session [B2CBB897208717EAE264C739E2D565BE] :: Hey, I'm still alive at Sat Mar 07 02:32:02 IRST 2020  
2020-03-07 02:32:56.580  WARN 6336 --- com.mypackage.MainView : Just received a SessionDestroy Event with SessionId :: [B2CBB897208717EAE264C739E2D565BE]  
2020-03-07 02:33:02.891  INFO 6336 --- com.mypackage.Zombie : Session [B2CBB897208717EAE264C739E2D565BE] :: Hey, I'm still alive at Sat Mar 07 02:33:02 IRST 2020  
2020-03-07 02:34:02.892  INFO 6336 --- com.mypackage.Zombie : Session [B2CBB897208717EAE264C739E2D565BE] :: Hey, I'm still alive at Sat Mar 07 02:34:02 IRST 2020

标签: javavaadinsession-timeout

解决方案


您在另一个问题中提到了垃圾收集。在大多数情况下,会话被销毁后,它会被垃圾回收。

当 Java GC 运行时,它可能会对无法通过垃圾收集根访问的所有内容进行垃圾收集。线程是一种根,只要它们在运行,就永远不会被垃圾回收。

Vaadin 不知道您对线程的意图是什么,也不知道如何安全地阻止它们。因此,您有责任阻止他们。

通常有两种方法可以停止线程,使用interrupt()或使用您自己的标志。

您应该使用哪一个取决于您的用例。如果您的线程正在休眠或等待,中断将立即中断它(顾名思义)并InterruptedException抛出一个。

如果您采用这种方法,请将您的更改while(true)while(!Thread.interrupted()),然后简单地调用zombie.interrupt()以停止它。如果线程当前正在休眠, catch则将到达您的块,因此如果它是正常行为,您可能不需要记录异常。

注意Thread.interrupted()在它被调用后重置中断标志,所以你不应该,例如,在 catch 块中调用它。isInterrupted()如果您想在不重置状态的情况下读取状态,则可以使用。

另一种选择是使用您自己的标志。最简单的方法是添加一个private volatile boolean stopped = false,然后将您的更改while (true)while(!stopped).

volatile关键字确保该值不会缓存在 CPU 中,这可能会延迟您的线程注意到值更改所需的时间。

然后,您可以添加一个方法来停止它,例如public void stop() { this.stopped = true; },并在会话销毁侦听器中调用它。由于这种方法不会中断等待的线程,因此它将始终完成循环的当前迭代,包括当前处于睡眠状态时的睡眠。


推荐阅读