【Java】问题总结集锦

最近遇到的一些问题,趁着闲下来的一点时间,做了一些小实验,来解答一些疑问。

(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(这一点我们可以通过任务管理器来查看)
具体两者的差别,移步:

java和 javaw 以及 javaws的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值