面对焦虑的最好办法就是动手去解决它一、问题描述
在启动定时任务quartz时,console后台显示错误,内容如下:
Exception in thread "Thread-26" java.lang.IllegalStateException: Executor has been shut down at org.apache.sshd.common.util.ValidateUtils.createFormattedException(ValidateUtils.java:213) at org.apache.sshd.common.util.ValidateUtils.throwIllegalStateException(ValidateUtils.java:207) at org.apache.sshd.common.util.ValidateUtils.checkState(ValidateUtils.java:184) at org.apache.sshd.common.util.threads.NoCloseExecutor.execute(NoCloseExecutor.java:100) at sun.nio.ch.AsynchronousChannelGroupImpl.executeOnPooledThread(AsynchronousChannelGroupImpl.java:188) at sun.nio.ch.Invoker.invokeIndirectly(Invoker.java:212) at sun.nio.ch.Invoker.invoke(Invoker.java:188) at sun.nio.ch.Invoker.invoke(Invoker.java:297) at sun.nio.ch.WindowsAsynchronousSocketChannelImpl$ReadTask.failed(WindowsAsynchronousSocketChannelImpl.java:600) at sun.nio.ch.Iocp$EventHandlerTask.run(Iocp.java:399) at java.lang.Thread.run(Thread.java:748)二、问题分析
(1)根据后台报错内容—Executor has been shut down,大致可以理解为程序创建的线程被关闭了,意思即为任务还在进行中,但是线程已经被意外关闭。其中报错的堆栈信息大致如下:
Thread.run -> WindowsAsynchronousSocketChannelImpl$ReadTask.failed -> NoCloseExecutor.execute -> ValidateUtils.checkState -> throwIllegalStateException
基本可以确定是线程意外关闭,导致异常抛出;
(2)仔细排除错误代码可以看到,是由org.apache.sshd.common依赖包提示异常错误,联想到项目代码中使用了SSH协议去连接服务器,所以暂时将问题锁定在SSHCLient类中;
(3)由于是意外关闭,可以理解是关闭ssh连接的错误操作造成的,查看SSHCLient类的close()方法
public void close() {
if (session != null){
try {
session.close();
} catch (Exception e){}
session = null;
}
if (sshClient != null){
try {
sshClient.stop();
} catch (Exception e){}
sshClient = null;
}
}
可以看到close()方法主要进行两个操作,一是判断session是否为空,不为空则值为空;二是判断sshClient实例是否为空地址,不为则置为空。
(4)对程序进行debug,查看session和sshClient两个对象的信息,如下
从debug出的对象可以看出,session和sshClient分别记录两种不同的信息。分别记录当前的程序执行信息。
(5)仔细查看两种对象的信息,其中sshClient中有以下信息:
而session的信息如下:
可以总结为sshClient包含两种ServiceFactories:记录用户认证信息的进程(UserAuth)以及客户端连接的进程(Connection)。而当前session是客户端连接的进程(Connection)。
(6)因此可以知道,close()方法分别关闭session和sshClient,导致连接信息的进程注销两次被调用,虽然能完成后面的业务信息,但是连接信息由于多次被注销,会提示进程已经被关闭的错误提示。
三、总结Executor has been shut down的问题来源于调用了已经被关闭的进程,属于二次调用的问题。上述问题出现在SSH连接的场景中,可以推广到更为广泛的多线程场景中。



