正常情况下,会优先采用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. 玩转X-CTR100 l STM32F4 l BMP280气压计传感器

    我造轮子,你造车,创客一起造起来!塔克创新资讯[塔克社区 www.xtark.cn ][塔克博客 www.cnblogs.com/xtark/ ]      本文介绍X-CTR100控制器 扩展BMP ...

  2. <youcengcibianli>

    #include<stdio.h> #include<stdlib.h> #include<string.h> #define num 100 #define OK ...

  3. $(function(){})与(function($){})(jquery)

    $(function(){}) function中的代码在DOM加载完成后立即执行,比window.onload()更快,因为前者不需要等待图片加载完成. (function($){})(jquery ...

  4. Ubuntu:替换DASH图标

    替换DASH图标 替换ubuntu搜索的图标 默认图标位置 备份 sudo mv /usr/share/unity/icons/launcher_bfb.png /usr/share/unity/ic ...

  5. mdadm 创建md 删除md步骤

    最近在使用mdadm创建和删除RAID设备.但是在创建和删除过程中会出现创建md0重启后变成md127,删除md127重启后又重新出现的状况.在网上搜索了一下,总结如下:   创建: 1.  mdad ...

  6. Iterator、Iteratable与ListIterator

    Iteratable: public interface Iterable<T> { Iterator<T> iterator(); default void forEach( ...

  7. L2-009. 抢红包

    没有人没抢过红包吧…… 这里给出N个人之间互相发红包.抢红包的记录,请你统计一下他们抢红包的收获. 输入格式: 输入第一行给出一个正整数N(<= 104),即参与发红包和抢红包的总人数,则这些人 ...

  8. Hign-Speed Tracking with Kernelzied Correlation Filters

    reference:Hign-Speed Tracking with Kernelzied Correlation Filters questions: The core componet of mo ...

  9. 人脸对齐matlab实现-FaceAlignment 3000fps

    前言 最近研读了孙剑团队的Face Alignment at 3000fps via Regressing Local Binary Features这篇paper,基于matlab进行实现. 实现原 ...

  10. 从 0 到 1 合理高效使用 GitHub 的资料

    来自:https://github.com/xirong/my-git/blob/master/how-to-use-github.md 说明 作为一名开发者,Github上面有很多东西值得关注学习, ...