正常情况下,会优先采用catalina.sh stop来停止Tomcat实例,这样可以让服务有机会处理完请求,并做好善后工作。 但如果通过catalina.sh stop命令无法关闭Tomcat实例,则只能kill -9了。

为什么在给Tomcat发出stop命令以后,Tomcat实例无法关闭?

可能有两种原因:

  • Tomcat的主线程没有结束(也即main函数没有执行结束);
  • Tomcat中启动的webapps有非daemon线程阻止了Tomcat进程的关闭;

第一种情况,如果发出stop命令以后,Tomcat主线程并没有结束,自然通过它启动的webapps也是无法关闭的。虽然可以确信Tomcat不可能有这个问题,但还是拉出Tomcat的关闭过程代码看看:

  1. public void start() {
  2. if (getServer() == null) {
  3. load();
  4. }
  5. if (getServer() == null) {
  6. log.fatal("Cannot start server. Server instance is not configured.");
  7. return;
  8. }
  9. long t1 = System.nanoTime();
  10. // Start the new server
  11. try {
  12. getServer().start();
  13. } catch (LifecycleException e) {
  14. log.fatal(sm.getString("catalina.serverStartFail"), e);
  15. try {
  16. getServer().destroy();
  17. } catch (LifecycleException e1) {
  18. log.debug("destroy() failed for failed Server ", e1);
  19. }
  20. return;
  21. }
  22. ……//省略
  23. if (await) {
  24. await();
  25. stop();
  26. }
  27. }

首先需要清楚,Tomcat的正常关闭是通过socket发送命令的方式来触发的,Tomcat在启动完成以后会通过await()一直等待,知道接收到shutdown命令后退出,执行后面的stop()关闭Tomcat实例。

此后不会再有任何await,所以说Tomcat主线程是会正常关闭的。如果不相信,可以开启jpda debug一下,我就这么干了。

既然第一种可能不存在,那只能是webapps中有非daemon线程没有正常关闭了。为什么会这样?因为非Daemon线程被认为是工作线程,必须要主动关闭,而daemon线程属于后台线程,在非daemon线程关闭以后,daemon线程会自动关闭,典型的以main函数为入口的主线程便是非daemon的工作线程。

一个示例:

  1. public static void main(String[] args) {
  2. Thread thread = new Thread(() -> {
  3. while(true){
  4. LockSupport.parkNanos(1000 * 1000 * 3);
  5. }
  6. });
  7. thread.setDaemon(true);
  8. thread.start();
  9. try {
  10. Thread.sleep(1000 * 3);
  11. } catch (Exception e) {
  12. e.printStackTrace();
  13. }
  14. }

这里启动了一个非daemon线程,所以即使主线程执行完成以后,应用还是不会正常关闭,如果把线程改成daemon则会。

基于这个原理可以开始定位为什么Tomcat示例无法关闭了,可以通过jstack看看还有那些线程还在运行,之后逐一排除。

最终发现是Java的线程池引起的,用Executors new了一个线程池,因为默认情况下,Executors使用了它自己的默认ThreadFactory,这个东西有毒,它new出来的线程是这样的:

  1. public Thread newThread(Runnable r) {
  2. Thread t = new Thread(group, r,
  3. namePrefix + threadNumber.getAndIncrement(),
  4. 0);
  5. if (t.isDaemon())
  6. t.setDaemon(false);
  7. if (t.getPriority() != Thread.NORM_PRIORITY)
  8. t.setPriority(Thread.NORM_PRIORITY);
  9. return t;
  10. }

这些线程都是非daemon的,所以通过这个线程池submit了任务以后,如果不主动调用线程池的shutdown()函数是无法destroy这些线程的。

从Tomcat无法正常关闭讲讲Java线程关闭问题【转载】的更多相关文章

  1. 50 道 Java 线程面试题(转载自牛客网)

    下面是 Java 线程相关的热门面试题,你可以用它来好好准备面试. 1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理 ...

  2. java 线程关闭小结(转)

    首先,要说的是java中没有一种停止线程的方法是绝对安全的.线程的中断Thread.interrput()方法很容易给人一种误会,让人感觉是一个线程使另外一个正在运行的线程停止工作,但实际上inter ...

  3. 深入了解java线程池(转载)

    出处:http://www.cnblogs.com/dolphin0520/ 本文归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责 ...

  4. java线程安全之并发Queue

    关闭 原 java线程安全之并发Queue(十三) 2017年11月19日 23:40:23 小彬彬~ 阅读数:12092更多 所属专栏: 线程安全    版权声明:本文为博主原创文章,未经博主允许不 ...

  5. 如何优雅的关闭Java线程池

    面试中经常会问到,创建一个线程池需要哪些参数啊,线程池的工作原理啊,却很少会问到线程池如何安全关闭的. 也正是因为大家不是很关注这块,即便是工作三四年的人,也会有因为线程池关闭不合理,导致应用无法正常 ...

  6. Java线程池理解及用法

    前言 多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担.线程本身也要占用内存空间,大量的线程会占用内存资源并且可能会导致Out of Memory ...

  7. Java线程池原理解读

    引言 引用自<阿里巴巴JAVA开发手册> [强制]线程资源必须通过线程池提供,不允许在应用中自行显式创建线程. 说明:使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销 ...

  8. 【Java并发专题之二】Java线程基础

    使用线程更好的提高资源利用率,但也会带来上下文切换的消耗,频繁的内核态和用户态的切换消耗,如果代码设计不好,可能弊大于利. 一.线程 进程是分配资源的最小单位,线程是程序执行的最小单位:线程是依附于进 ...

  9. java线程池实践

    线程池大家都很熟悉,无论是平时的业务开发还是框架中间件都会用到,大部分都是基于JDK线程池ThreadPoolExecutor做的封装, 都会牵涉到这几个核心参数的设置:核心线程数,等待(任务)队列, ...

随机推荐

  1. block ,GCD(转)

    原文:http://blog.sina.com.cn/s/blog_45e2b66c01010dhd.html 1.GCD之dispatch queue http://www.cnblogs.com/ ...

  2. WebGL编程指南理论分析之物体的运动和点光源

    之前的所有案例中物体都是静止的并且是平行光照射,这里我们来讨论一下运动物体和点光源. 一.运动物体 我们知道对于一个提供原始顶点坐标和原始顶点法向量数据的着色器绘制出来的图形看上去是这样的: 看上去很 ...

  3. tensorflow读取训练数据方法

    1. 预加载数据 Preloaded data # coding: utf-8 import tensorflow as tf # 设计Graph x1 = tf.constant([2, 3, 4] ...

  4. ldconfig

    #ldconfig# http://www.cnblogs.com/lyongde/p/4190588.html ldconfig是一个动态链接库管理命令,为了让动态链接库为系统所共享,还需运行动态链 ...

  5. stm32内部温度计算方法

    计算公式:Temperature  = {(V25 - VSENSE) / Avg_Slope} + 25 V25              最小=1.34V     典型=1.43V   最大=1. ...

  6. apache flink docker-compose 运行试用

    apache 是一个流处理框架,官方提供了docker 镜像,同时也提供了基于docker-compose 运行的说明 docker-compose file version: "2.1&q ...

  7. sdut2165 Crack Mathmen (山东省第二届ACM省赛)

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/svitter/article/details/24270265 本文出自:http://blog.c ...

  8. WAJUEJI which home strong!nyoj

    WAJUEJI which home strong! 时间限制:1000 ms  |  内存限制:65535 KB 难度:2   描述 在一个山沟里,姐弟俩同时考上了大学.但由于家里拮据,所以这并不是 ...

  9. 记录 ThinkPHP 5.* 漏洞修复后的情况

    记录 ThinkPHP 5.* 漏洞修复后的情况 ThinkPHP 官方 2018-12-09 下午收到漏洞报告. 2018-12-09 晚上看到 Git 已经更新了,修复了漏洞. 2018-12-1 ...

  10. os层删除与数据库层drop的区别于对策。

    os rm删除的,可以通过os层面恢复(句柄.inode.如果inode没有了可以根据block去重组出来数据文件),也可以通过rman备份还原方式恢复.  db 层面drop删除的 只能不完全恢复( ...