最近遇到的一些问题,趁着闲下来的一点时间,做了一些小实验,来解答一些疑问。
(1)直接关闭tomcat和利用shutdown.bat关闭区别
(2)在ServletContextListener中的contextDestroyed()方法的作用,在里面销毁线程与不销毁的区别
(3)调用contextDestroyed()方法需要在shutdown.bat运行之后进行,直接关闭startup.bat窗口是不会调用contextDestroyed ()方法的。
(4)listener调用的顺序是按照web.xml配置顺序来加载的,顺序执行。
(5)java.exe和javaw.exe的区别
针对上面的问题,统一用一个例子来作出解释
下面针对上面的问题逐一作出解答
代码的框架如下:
测试代码:
测试代码请从这儿下载
测试代码下载地址
Listener1.java
package test;
import java.util.Date;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class Listener1 implements ServletContextListener{
@Override
public void contextDestroyed(ServletContextEvent sce) {
// TODO Auto-generated method stub
}
@Override
public void contextInitialized(ServletContextEvent sce) {
// TODO Auto-generated method stub
try {
Thread.sleep(2000);
System.out.println(new Date());
System.out.println("Listener1已经休息了2秒");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Listener2.java
package test;
import java.util.Date;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class Listener2 implements ServletContextListener{
@Override
public void contextDestroyed(ServletContextEvent sce) {
// TODO Auto-generated method stub
}
@Override
public void contextInitialized(ServletContextEvent sce) {
// TODO Auto-generated method stub
try {
Thread.sleep(2000);
System.out.println(new Date());
System.out.println("Listener2已经休息了2秒");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
SleepListener.java
package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class SleepListener implements ServletContextListener{
ExecutorService executor1=Executors.newFixedThreadPool(3);
@Override
public void contextDestroyed(ServletContextEvent sce) {
// TODO Auto-generated method stub
executor1.shutdownNow();
// executor1.shutdown();
System.out.println("已经关闭!");
}
@Override
public void contextInitialized(ServletContextEvent sce) {
task1 task1=new task1();
task2 task2=new task2();
task3 task3=new task3();
executor1.submit(task1);
executor1.submit(task2);
executor1.submit(task3);
while(task1.getis()!=true||task2.getis()!=true||task3.getis()!=true){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
task1.java
package test;
import java.util.concurrent.Callable;
public class task1 implements Callable<String>{
boolean is=false;
@Override
public String call() throws Exception {
while(true){
Thread.sleep(7000);
System.out.println("task1");
is=true;
}
}
public boolean getis(){
return is;
}
}
task2.java
package test;
import java.util.concurrent.Callable;
public class task2 implements Callable<String>{
boolean is=false;
@Override
public String call() throws Exception {
while(true){
Thread.sleep(8000);
System.out.println("task2");
is=true;
}
}
public boolean getis(){
return is;
}
}
task3.java
package test;
import java.util.concurrent.Callable;
public class task3 implements Callable<String>{
boolean is=false;
@Override
public String call() throws Exception {
while(true){
Thread.sleep(3000);
System.out.println("task3");
is=true;
}
}
public boolean getis(){
return is;
}
}
(1)叉掉startup.bat窗口和利用shutdown.bat窗口关闭tomcat的区别
直接叉掉startup.bat窗口,java.exe进程会直接被杀死(可以通过任务管理器观察)
而利用shutdown.bat,tomcat在关闭之前会进行一些操作,其中一项操作就是在ServletContextListener中,会执行contextDestroyed()中的代码块。
因此,可以将一些需要释放的资源,关闭线程等任务放在这个里面。如同上面的SleepListener.java中的contextDestroyed()函数,可以执行相应的关闭进程的代码(上面使用了线程池ExecutorService的shutdownNow()方法,可以立刻关闭进程,而不会等待线程执行完毕,但是shutdown()方法却不会立刻关闭线程,而是会等线程执行完毕之后再关闭)
下面贴出shutdown()和shutdownNow()的方法的注释
/**
* Initiates an orderly shutdown in which previously submitted
* tasks are executed, but no new tasks will be accepted.
* Invocation has no additional effect if already shut down.
*
* <p>This method does not wait for previously submitted tasks to
* complete execution. Use {@link #awaitTermination awaitTermination}
* to do that.
*
* @throws SecurityException if a security manager exists and
* shutting down this ExecutorService may manipulate
* threads that the caller is not permitted to modify
* because it does not hold {@link
* java.lang.RuntimePermission}{@code ("modifyThread")},
* or the security manager's {@code checkAccess} method
* denies access.
*/
void shutdown();
/**
* Attempts to stop all actively executing tasks, halts the
* processing of waiting tasks, and returns a list of the tasks
* that were awaiting execution.
*
* <p>This method does not wait for actively executing tasks to
* terminate. Use {@link #awaitTermination awaitTermination} to
* do that.
*
* <p>There are no guarantees beyond best-effort attempts to stop
* processing actively executing tasks. For example, typical
* implementations will cancel via {@link Thread#interrupt}, so any
* task that fails to respond to interrupts may never terminate.
*
* @return list of tasks that never commenced execution
* @throws SecurityException if a security manager exists and
* shutting down this ExecutorService may manipulate
* threads that the caller is not permitted to modify
* because it does not hold {@link
* java.lang.RuntimePermission}{@code ("modifyThread")},
* or the security manager's {@code checkAccess} method
* denies access.
*/
List<Runnable> shutdownNow();
具体查看
ExecutorService对象的shutdown()和shutdownNow()的区别
(2)在ServletContextListener中的contextDestroyed()方法的作用,在里面销毁线程与不销毁的区别
这部分已经在上面的(1)中已经阐述
(3)调用contextDestroyed()方法需要在shutdown.bat运行之后进行,直接关闭startup.bat窗口是不会调用contextDestroyed ()方法的
这个可以通过上面的测试代码,经过测试就可以观察得到。
(4)listener调用的顺序是按照web.xml配置顺序来加载的,顺序执行。
web.xml的内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<!--
<resource-ref>
<description>databaseConnection</description>
<res-ref-name>taxi</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
-->
<listener>
<listener-class>test.Listener1</listener-class>
</listener>
<listener>
<listener-class>test.SleepListener</listener-class>
</listener>
<listener>
<listener-class>test.Listener2</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
可以看出来上面的Listener的顺序是Listener1、SleepListener、Listener2
结果运行的截图:
从运行结果可以看出来,启动顺序就是按照Listener1–>SleepListener–>Listener2来的。
延伸知识:
web.xml 中的listener、 filter、servlet 加载顺序及其详解
(5)java.exe和javaw.exe的区别
java.exe执行的是控制台程序,javaw.exe执行的是窗体程序
例如,我们利用tomcat的startup.bat启动的是java.exe,而利用Myeclipse启动的是javaw.exe(这一点我们可以通过任务管理器来查看)
具体两者的差别,移步: