网页访问服务器tomcat一直在响应,Tomcat假死,无法响应前端请求原因分析

本文探讨了Tomcat服务器在响应前端请求时可能出现的问题,特别是由于无限循环或未正确处理的线程导致的假死状态。为了解决这个问题,建议将线程设置为后台线程,并利用Shutdown Hook在JVM关闭时确保线程能够优雅地关闭,防止数据丢失和不必要的系统重启。文章提供了一个示例,展示了如何创建和注册Shutdown Hook来协调线程的关闭过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

# 使用后台进程和 Shutdown Hook 友好地关闭 Tomcat

严重的问题就是在 JVM 关闭时,行为不良的线程不会被关闭。

你可能会问:为什么这会成为问题……好吧,对程序员来说这真的算不上一个问题,只要随便写点代码就可以解决。但是对使用软件的人而言这却会带来不必要的麻烦。原因是这样会产生很多行为不良的线程,而执行 Tomcat 的 shutdown.sh 命令收效甚微。这时你不得不执行下面命令野蛮的杀掉 web 服务器:

ps -ef | grepjava

先得到进程 pid,然后

kill -9 <>

……接着需要有一大片 web 服务器需要重启,这种混乱绝对让人头痛。最后你执行 shutdown.sh 停止 Tomcat。

在我最近的几篇博客里,我编写的那些行为不良的线程在 run() 方法开头都包含了下面的代码:

Override

public void run() {

while (true) {

try {

DeferredResult result = resultQueue.take();

Message message = queue.take();

result.setResult(message);

} catch (InterruptedException e) {

throw new UpdateException("Cannot get latest update. " + e.getMessage(), e);

}

}

}

在上面的代码里,我用了一个无限循环 while(true),这意味着线程会一直运行并且不会终止。

@Override

public void run() {

sleep(5); // Sleep等待app重新加载

logger.info("The match has now started...");

long now = System.currentTimeMillis();

List matchUpdates = match.getUpdates();

for (Message message : matchUpdates) {

delayUntilNextUpdate(now, message.getTime());

logger.info("Add message to queue: {}", message.getMessageText());

queue.add(message);

}

start = true; // 结束,重启

logger.warn("GAME OVER");

}

上面第二个示例中线程的行为同样很糟糕。线程会从 MatchUpdates 列表中取消息并在合适的时候添加到消息队列。唯一的可取之处是他们会抛出异常 InterruptedException,如果正确处理线程可以正常停止。然而,没有人能确保这一点。

上面代码的一个有效地快速修正……只要确保创建所有线程都是后台线程。后台线程的定义是:在程序结束时,即使线程还在运行但不会阻止 JVM 退出。一个后台线程的例子就是 JVM 的垃圾回收线程。将线程设置为后台线程只需要调用:

thread.setDaemon(true);

……接着执行 shutdown.sh,然后砰的一声所有的线程都消失了。然而这种做法有一个问题:如果你的后台线程正在执行重要的任务,刚刚开始执行就被突然结束掉会导致丢失很多重要的数据该怎么办?

你需要确保所有线程都被友好地关闭,在关闭前完成所有正在执行的任务。本文接下来将为这些错误的线程给出一个修复,使用 ShutdownHook 让他们在关闭前互相协调。根据文档的描述:“一个 shutdown hook 就是一个初始化但没有启动的线程。 当虚拟机开始执行关闭程序时,它会启动所有已注册的 shutdown hook(不按先后顺序)并且并发执行。” 读完最后一句话,你可能已经猜到了你需要的就是创建一个负责关闭多有其他线程的线程并通过 shutdown hook 传递给 JVM。只要在你已有线程的 run() 方法里用几个小的 class 做一些手脚。

需要创建 ShutdownService 和 Hook 两个类。首先展示的是 Hook 类,它会将 ShutdownService 连接到你的线程,代码如下:

public class Hook {

private static final Logger logger = LoggerFactory.getLogger(Hook.class);

private boolean keepRunning = true;

private final Thread thread;

Hook(Thread thread) {

this.thread = thread;

}

/**

* @return True 如果后台线程继续运行

*/

public boolean keepRunning() {

return keepRunning;

}

/**

* 告诉客户端后台线程关闭并等待友好地关闭

*/

public void shutdown() {

keepRunning = false;

thread.interrupt();

try {

thread.join();

} catch (InterruptedException e) {

logger.error("Error shutting down thread with hook", e);

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值