java线程在javaagent阻止退出中启动
在javagent中,我启动了一个HttpServer:
public static void premain(String agentArgs, Instrumentation inst) throws InstantiationException, IOException {
HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
server.createContext("/report", new ReportHandler());
server.createContext("/data", new DataHandler());
server.createContext("/stack", new StackHandler());
ExecutorService es = Executors.newCachedThreadPool(new ThreadFactory() {
int count = 0;
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("JDBCLD-HTTP-SERVER" + count++);
return t;
}
});
server.setExecutor(es);
server.start();
// how to properly close ?
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
server.stop(5);
log.info("internal httpserver has been closed.");
es.shutdown();
try {
if (!es.awaitTermination(60, TimeUnit.SECONDS)) {
log.warn("executor service of internal httpserver not closing in 60 seconds");
es.shutdownNow();
if (!es.awaitTermination(60, TimeUnit.SECONDS))
log.error("executor service of internal httpserver not closing in 120 seconds, give up");
}else {
log.info("executor service of internal httpserver closed.");
}
} catch (InterruptedException ie) {
log.warn("thread interrupted, shutdown executor service of internal httpserver");
es.shutdownNow();
Thread.currentThread().interrupt();
}
}
});
// other instrumention code ignored ...
}
测试计划:
public class AgentTest {
public static void main(String[] args) throws SQLException {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:oracle:thin:@172.31.27.182:1521/pas");
config.setUsername("pas");
config.setPassword("pas");
HikariDataSource ds = new HikariDataSource(config);
Connection c = ds.getConnection();
Connection c1 = ds.getConnection();
c.getMetaData();
try {
Thread.sleep(1000 * 60 * 10);
} catch (InterruptedException e) {
e.printStackTrace();
c.close();
c1.close();
ds.close();
}
c.close();
c1.close();
ds.close();
}
}
当目标jvm退出时,我希望停止HttpServer。但当我的测试java程序完成时,主线程停止,但整个jvm进程不会终止,上面代码中的shutdown hook不会执行。如果我在eclipse IDE中单击“终止”按钮,eclipse将显示一个错误: 但至少jvm会退出,我的关闭钩子会被调用
根据java.lang.Runtime
的java文档:
The Java virtual machine shuts down in response to two kinds of events:
The program exits normally, when the last non-daemon thread exits or when the exit (equivalently, System.exit) method is invoked, or The virtual machine is terminated in response to a user interrupt, such as typing ^C, or a system-wide event, such as user logoff or system shutdown.
com.sun.net.httpserver.HttpServer
将启动一个非守护进程调度程序线程,该线程将在调用HttpServer#stop
时退出,因此我面临死锁
non-daemon thread not finish -> shutdown hook not triggered -> can't stop server -> non-daemon thread not finish
有什么好主意吗?请注意,我无法修改目标应用程序的代码
应用kriegaex答案后的更新
I added some logging to watch dog thread, and here is outputs:
2021-09-22 17:30:00.967 INFO - Connnection@1594791957 acquired by 40A4F128987F8BD9C0EE6749895D1237
2021-09-22 17:30:00.968 DEBUG - Stack@40A4F128987F8BD9C0EE6749895D1237:
java.lang.Throwable:
at com.zaxxer.hikari.pool.ProxyConnection.<init>(ProxyConnection.java:102)
at com.zaxxer.hikari.pool.HikariProxyConnection.<init>(HikariProxyConnection.java)
at com.zaxxer.hikari.pool.ProxyFactory.getProxyConnection(ProxyFactory.java)
at com.zaxxer.hikari.pool.PoolEntry.createProxyConnection(PoolEntry.java:97)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:192)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:162)
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:100)
at agenttest.AgentTest.main(AgentTest.java:19)
2021-09-22 17:30:00.969 INFO - Connnection@686560878 acquired by 464555C270688B747CA211DE489B7730
2021-09-22 17:30:00.969 DEBUG - Stack@464555C270688B747CA211DE489B7730:
java.lang.Throwable:
at com.zaxxer.hikari.pool.ProxyConnection.<init>(ProxyConnection.java:102)
at com.zaxxer.hikari.pool.HikariProxyConnection.<init>(HikariProxyConnection.java)
at com.zaxxer.hikari.pool.ProxyFactory.getProxyConnection(ProxyFactory.java)
at com.zaxxer.hikari.pool.PoolEntry.createProxyConnection(PoolEntry.java:97)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:192)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:162)
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:100)
at agenttest.AgentTest.main(AgentTest.java:20)
2021-09-22 17:30:00.971 DEBUG - Connnection@1594791957 used by getMetaData
2021-09-22 17:30:01.956 DEBUG - there is still 12 active threads, keep wathcing
2021-09-22 17:30:01.956 DEBUG - Reference Handler#true Finalizer#true Signal Dispatcher#true main#false server-timer#true Thread-2#false jdbcld-watch-dog#false Timer-0#true oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser#true InterruptTimer#true HikariPool-1 housekeeper#true HikariPool-1 connection adder#true
2021-09-22 17:30:02.956 DEBUG - there is still 12 active threads, keep wathcing
2021-09-22 17:30:02.956 DEBUG - Reference Handler#true Finalizer#true Signal Dispatcher#true main#false server-timer#true Thread-2#false jdbcld-watch-dog#false Timer-0#true oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser#true InterruptTimer#true HikariPool-1 housekeeper#true HikariPool-1 connection adder#true
2021-09-22 17:30:03.957 DEBUG - there is still 12 active threads, keep wathcing
2021-09-22 17:30:03.957 DEBUG - Reference Handler#true Finalizer#true Signal Dispatcher#true main#false server-timer#true Thread-2#false jdbcld-watch-dog#false Timer-0#true oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser#true InterruptTimer#true HikariPool-1 housekeeper#true HikariPool-1 connection adder#true
2021-09-22 17:30:04.959 DEBUG - there is still 12 active threads, keep wathcing
2021-09-22 17:30:04.959 DEBUG - Reference Handler#true Finalizer#true Signal Dispatcher#true main#false server-timer#true Thread-2#false jdbcld-watch-dog#false Timer-0#true oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser#true InterruptTimer#true HikariPool-1 housekeeper#true HikariPool-1 connection adder#true
2021-09-22 17:30:05.959 DEBUG - there is still 12 active threads, keep wathcing
2021-09-22 17:30:05.960 DEBUG - Reference Handler#true Finalizer#true Signal Dispatcher#true main#false server-timer#true Thread-2#false jdbcld-watch-dog#false Timer-0#true oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser#true InterruptTimer#true HikariPool-1 housekeeper#true HikariPool-1 connection adder#true
2021-09-22 17:30:06.960 DEBUG - there is still 11 active threads, keep wathcing
2021-09-22 17:30:06.960 DEBUG - Reference Handler#true Finalizer#true Signal Dispatcher#true main#false server-timer#true Thread-2#false jdbcld-watch-dog#false Timer-0#true oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser#true InterruptTimer#true HikariPool-1 housekeeper#true
2021-09-22 17:30:07.961 DEBUG - there is still 11 active threads, keep wathcing
2021-09-22 17:30:07.961 DEBUG - Reference Handler#true Finalizer#true Signal Dispatcher#true main#false server-timer#true Thread-2#false jdbcld-watch-dog#false Timer-0#true oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser#true InterruptTimer#true HikariPool-1 housekeeper#true
2021-09-22 17:30:08.961 DEBUG - there is still 11 active threads, keep wathcing
2021-09-22 17:30:08.961 DEBUG - Reference Handler#true Finalizer#true Signal Dispatcher#true main#false server-timer#true Thread-2#false jdbcld-watch-dog#false Timer-0#true oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser#true InterruptTimer#true HikariPool-1 housekeeper#true
2021-09-22 17:30:09.962 DEBUG - there is still 11 active threads, keep wathcing
2021-09-22 17:30:09.962 DEBUG - Reference Handler#true Finalizer#true Signal Dispatcher#true main#false server-timer#true Thread-2#false jdbcld-watch-dog#false Timer-0#true oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser#true InterruptTimer#true HikariPool-1 housekeeper#true
2021-09-22 17:30:10.962 DEBUG - there is still 11 active threads, keep wathcing
2021-09-22 17:30:10.963 DEBUG - Reference Handler#true Finalizer#true Signal Dispatcher#true main#false server-timer#true Thread-2#false jdbcld-watch-dog#false Timer-0#true oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser#true InterruptTimer#true HikariPool-1 housekeeper#true
2021-09-22 17:30:10.976 INFO - Connnection@1594791957 released
2021-09-22 17:30:10.976 DEBUG - set connection count to 0 by stack hash 40A4F128987F8BD9C0EE6749895D1237
2021-09-22 17:30:10.976 INFO - Connnection@686560878 released
2021-09-22 17:30:10.976 DEBUG - set connection count to 0 by stack hash 464555C270688B747CA211DE489B7730
2021-09-22 17:30:11.963 DEBUG - there is still 10 active threads, keep wathcing
2021-09-22 17:30:11.963 DEBUG - Reference Handler#true Finalizer#true Signal Dispatcher#true server-timer#true Thread-2#false jdbcld-watch-dog#false Timer-0#true oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser#true InterruptTimer#true DestroyJavaVM#false
2021-09-22 17:30:12.964 DEBUG - there is still 10 active threads, keep wathcing
更新
我想支持各种java应用程序,包括使用servlet容器运行的web应用程序和标准的javase应用程序
# 1 楼答案
这里有一点MCVE说明了Ewramer的想法。我使用小的
byte-buddy-agent
帮助程序库动态附加一个代理,以使我的示例自包含,从主方法开始启动Java代理。我省略了运行这个示例所需的3个普通无操作虚拟处理程序类正如您所看到的,这基本上是您的示例代码,为了可读性,它被重构为几个helper方法,再加上新的watchdog方法。这很简单
这将生成如下控制台日志: