我正在尝试在 Controller 中创建新线程,该线程将对服务执行长时间运行的查询(listFromService = getListFromService();)。 目前,虽然我没有连接到服务。我通过执行 Thread.sleep(100000) 并使用虚拟数据填充 listFromService 来模拟此过程。
之后,我想将此列表放入 CSV 文件中并直接流式传输到 HttpServletResponse 响应。
这段代码在没有线程的情况下可以完美工作。但它阻塞了主线程,用户无法做任何事情。
@RequestMapping(value = "/download")
public void downloadCSV1(@RequestParam() final Map<String, String> params,
HttpServletResponse response) throws IOException
{
new Thread()
{
public void run() {
System.out.println("Before Running thread");
List<Objects> listFromService;
try {
Thread.sleep(10000);
listFromService = getListFromService();
response.setHeader("Content-disposition", "attachment;filename="+ "metrics.csv");
ServletOutputStream outputStream;
try {
outputStream = response.getOutputStream();
listFromService.stream().forEach(item -> {
try {
processMetricsDownloadListItem(item, outputStream);
} catch (IOException e) {
e.printStackTrace();
}
});
outputStream.flush();
} catch (IOException e1) {
e1.printStackTrace();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} // end or run()
}.start();
}
我收到的错误如下:
Exception in thread "Thread-5" java.lang.NullPointerException
[tomcat:launchProperties] at org.apache.coyote.http11.InternalNioOutputBuffer.flushBuffer(InternalNioOutputBuffer.java:235)
[tomcat:launchProperties] at org.apache.coyote.http11.InternalNioOutputBuffer.addToBB(InternalNioOutputBuffer.java:190)
[tomcat:launchProperties] at org.apache.coyote.http11.InternalNioOutputBuffer.commit(InternalNioOutputBuffer.java:178)
[tomcat:launchProperties] at org.apache.coyote.http11.AbstractHttp11Processor.action(AbstractHttp11Processor.java:750)
[tomcat:launchProperties] at org.apache.coyote.Response.action(Response.java:177)
[tomcat:launchProperties] at org.apache.coyote.http11.AbstractOutputBuffer.doWrite(AbstractOutputBuffer.java:249)
.........
最佳答案
当执行http方法调用时,servlet容器(例如Tomcat)创建一个专用线程来服务给定的请求。该线程执行多个操作,其中之一是调用 Controller 方法内部的逻辑。根据你的情况,它只是启动另一个线程。此后,容器线程继续其生命周期,将响应发送回客户端并最终关闭 HttpServletResponse。这与您的应用程序线程同时执行。因此,当您的应用程序唤醒并尝试刷新已被容器线程关闭的 OutputStream 时,它会收到错误。如果在 start() 之后添加 t.join(),代码的工作方式将与没有单独线程时的工作方式相同。
不幸的是,仅使用应用程序服务器无法释放客户端连接并稍后在不阻塞的情况下发送计算结果,因为客户端收到服务器响应后无法重用http连接。您应该在客户端使用异步工具(例如 AJAX)对服务器进行异步调用,并在结果准备好时采取操作。对服务器的调用始终处于阻塞状态,但在这种情况下,异步客户端线程被阻塞,并且您的主应用程序线程继续处理用户的操作。
关于java - spring mvc Controller 中的线程不工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46125619/