最近研究embeded tomcat,特别是关于tomcat启动和关闭的模块。通过查看相应的源代码,我们知道tomcat的关闭是通过往相应的关闭端口发送指定的关闭指令来达到关闭tomcat的目的。但是有的时候,通过shutdown.bat或shutdown.sh却不能有效地关闭tomcat,网上也有很多人提出这个问题。通过相关资料,最后问题出现线程上。
首先看java虚拟机退出的条件,如下所示:
|
a,调用了 Runtime 类的 exit 方法,并且安全管理器允许退出操作发生。
b,非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到 run 方法之外的异常。
|
如上所示,第一条是通过exit退出,第二条指出了一个正常程序退出的条件,就是所有的非守护线程都已经停止运行。我们看相应embed tomcat的启动代码,如下所示:
tomcat.start(); tomcat.getServer().await();
最后一条有效的运行命令即是await,通过调用shutdown命令时,这个await就会成功的返回。按照常理来说,整个程序即会成功的完成。但是程序有时候并没有成功的结束,原因就在于程序中还存在着非守护进程。
对于tomcat来说,tomcat程序中开启的所有进程都是守护进程,所以tomcat自身可以保证程序的正常结束。当await结束时,tomcat所就正常的结束了,包括相应的监听端口等,都已经成功结束。然而,由于项目程序中仍然还有其它线程在运行,所以导致java虚拟机并没有成功的退出。
在我们的项目中,很多时候都运用到了线程。比如,异步调用等。不过,幸运的是,这些线程往往都是守护线程,原因就在于tomcat在运行我们的项目时,对于每一个请求,tomcat是使用了守护线程来进行相应的请求调用,这个保证在以下代码:
// Start poller threads pollers = new Poller[pollerThreadCount]; for (int i = 0; i < pollerThreadCount; i++) { pollers[i] = new Poller(false); pollers[i].init(); Thread pollerThread = new Thread(pollers[i], getName() + "-Poller-" + i); pollerThread.setPriority(threadPriority); pollerThread.setDaemon(true); pollerThread.start();
所以,一般情况下,在我们的项目代码中使用new Thread建立的线程都是守护线程,原因就是新建线程默认上使用建立线程时的当前线程所处的守护状态。tomcat的请求处理线程为守护线程,所以我们一般情况下建立的线程也是守护线程。然而,Executors除外。
使用Executors建立后台线程并执行一些多线程操作时,Executors会使用相对应的threadFactory来对runnable建立新的thread,所以使用默认的threadFactory时就会出问题。默认的ThreadFactory强制性的将新创建的线程设置为非守护状态,如下所示:
public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; }
所以,一般情况下,我们使用executors创建多线程时,就会使用默认的threadFactory(即调用只有一个参数的工厂方法),而创建出来的线程就是非守护的。而相应的程序就永远不会退出,如采用Executors创建定时调度任务时,这个调试任务永远不会退出。解决的办法就是重写相对应的threadFactory,如下所示:
new ThreadFactory() { public Thread newThread(Runnable r) { Thread s = Executors.defaultThreadFactory().newThread(r); s.setDaemon(true); return s; } }
同理,对于java web项目中的线程程序,一定要记住将相应的线程标记为守护线程(尽管它默认就是守护的)。而对于使用Executors,一定要记住传递相应的threadFactory实现,以重写相应的newThread方法,将线程标记为守护线程。
以上的结论对于普通的java项目同样有效,为了正常的结束相应的程序,一定要正确的使用相应的线程,以避免java程序不能退出的问题。
转载请标明出处:i flym
本文地址:http://www.iflym.com/index.php/code/dont-use-executors-and-nondaemon-thread-in-java-web.html
特此更正在jdk1.6中,创建线程时,默认都是非守护线程了。
如Thread源码:
public Thread() { daemon = false; stillborn = false; threadLocals = null; inheritableThreadLocals = null; threadStatus = 0; blockerLock = new Object(); init(null, null, (new StringBuilder()).append("Thread-").append(nextThreadNum()).toString(), 0L); }
分享到:
相关推荐
Java多线程实现数据切割批量执行,实现限流操作。 java线程池Executors实现数据批量操作。 批量异步Executors处理数据,实现限流操作,QPS限流。 线程池调用第三方接口限流实现逻辑。 案例适合: 1.批量处理大数据。...
Java中Executors类中几种创建各类型线程池方法及简单实例
在Java编程环境中,高效地管理和控制并发执行是关键任务之一,尤其在处理多核处理器或者分布式系统时。Java提供了一套强大的并发工具,其中`java.util.concurrent.Executors`类是核心部分,它允许我们创建和管理...
Java并发编程中,`java.util.concurrent.Executors` 类是一个至关重要的工具类,它提供了一系列静态方法,用于创建和管理线程池以及相关的线程执行服务。`Executors` 类简化了线程池的创建和线程的调度,使得开发者...
在Java编程中,多线程查询数据库是一种常见的优化策略,特别是在处理大数据量或者需要并行执行多个查询时。本文将详细探讨如何利用Java的多线程技术和线程池来实现并发查询数据库,以及相关的文件`BatchDataUtil....
在本教程中,我们将深入探讨Java中的多线程设计模式、并发核心编程概念以及线程池的工作原理和种类。 首先,让我们了解什么是多线程。在单线程环境中,程序按照顺序执行任务,而在多线程环境中,可以同时执行多个...
Java Web 多线程下载器是一种利用Java编程语言实现的高效下载工具,它通过将大文件分割成多个部分,同时启动多个线程进行下载,从而显著提高了文件下载速度。这样的设计充分利用了网络带宽和系统资源,尤其在处理大...
3. 单线程线程池:单线程线程池是Java中的一个特殊的线程池,它只有一个线程,所有的任务都将在这个线程中执行。 ```java ExecutorService service = Executors.newSingleThreadExecutor(); ``` 4. 周期性任务定长...
总之,多线程Web服务器在Java中实现的核心在于合理调度线程,高效处理并发请求,并确保线程安全。通过线程池、并发控制机制以及高效的I/O模型,我们可以构建出能应对高并发场景的Web服务器。在实际项目中,还应考虑...
此外,书中可能讨论了线程池的概念,比如`ExecutorService`, `ThreadPoolExecutor`和`Executors`工厂方法,它们是Java中管理和控制线程的有效手段,可以提高性能并减少资源消耗。线程池允许设置线程数量、队列大小...
Java 中 Executor, ExecutorService 和 Executors 的不同 Java 中的 Executor, ExecutorService 和 Executors 是 Java Executor 框架的重要组件,用于提供线程池的功能。在 Java 1.5 之后,Executor 框架提供了多种...
本文将深入探讨Java项目中最简单的多线程使用方法,通过一个名为"thread-test"的示例项目来讲解如何在企业实际场景中应用多线程。 一、Java多线程基础 1. **Thread类与Runnable接口** Java中实现多线程有两种方式...
守护线程(Daemon Thread)是一种特殊线程,当所有非守护线程结束时,守护线程也会随之结束,通常用于后台服务。 总结: Java多线程编程是提升程序性能和响应性的关键技术。理解多线程的概念,掌握线程的创建、同步...
本示例着重探讨如何在Java中实现和管理多线程,以及它带来的挑战和解决方案。 一、Java多线程基础 1. 创建线程: - 继承Thread类:创建一个新的类,继承自Thread类,重写其run()方法,然后创建该类的实例并调用...
1. **ServletContextListener**:这是Java Web中的一种监听器,用于监听ServletContext对象的生命周期事件。我们可以通过实现`javax.servlet.ServletContextListener`接口,并重写`contextInitialized`和`...
Java多线程是Java编程中的重要概念,尤其在如今的多核处理器环境下,理解并熟练掌握多线程技术对于提高程序性能和响应速度至关重要。本资料详细讲解了Java多线程的原理,并提供了丰富的实战代码,非常适合Java初学者...
`java.util.concurrent.Executors` 是Java并发编程中一个非常重要的工具类,主要用于创建不同类型的线程池对象。通过使用`Executors`类,开发者可以方便地管理和控制线程池的行为,从而提高系统的性能和资源利用率。...
Java多线程是Java编程语言中一个非常重要的概念,它允许开发者在一个程序中创建多个执行线程并行运行,以提高程序的执行效率和响应速度。在Java中,线程的生命周期包含五个基本状态,分别是新建状态(New)、就绪...
**守护线程**:守护线程是为用户线程服务的,当所有非守护线程结束时,程序也会自动结束。 #### 八、Java线程:线程的同步-同步方法与同步块 - **同步方法** 通过在方法签名前添加 `synchronized` 关键字,可以...
在Java中,线程并发可以通过多种方式实现,包括继承Thread类、实现Runnable接口以及使用ExecutorService和Future等高级API。下面将详细探讨这些知识点。 首先,Java中创建线程主要有两种方法。一种是通过继承Thread...