Vaadin 14 - 如何确保会话相关对象在 SessionDestroy 之后得到清理
Vaadin 14 - How to make sure of session related objects get cleaned up after SessionDestroy
这是我在 Vaadin 14.1.18 SpringBoot 应用程序中的 MainView Class。
@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
}
}
我还实现了一个 Zombie 进程,如下所示,我在我的 MainView Class 中对其进行了实例化。
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
您在另一个问题中提到了垃圾收集。在大多数情况下,会话被销毁后,它将被垃圾收集。
当 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; }
,并在会话中调用销毁侦听器。由于这种方法不会中断正在等待的线程,因此它将始终完成循环的当前迭代,如果当前正在休眠,则包括休眠。
这是我在 Vaadin 14.1.18 SpringBoot 应用程序中的 MainView Class。
@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
}
}
我还实现了一个 Zombie 进程,如下所示,我在我的 MainView Class 中对其进行了实例化。
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
您在另一个问题中提到了垃圾收集。在大多数情况下,会话被销毁后,它将被垃圾收集。
当 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; }
,并在会话中调用销毁侦听器。由于这种方法不会中断正在等待的线程,因此它将始终完成循环的当前迭代,如果当前正在休眠,则包括休眠。