所以,让我们说我有一个网络应用程序,并且我们为每个请求生成一个新线程。数百个请求进入,在Web服务器代码中的某个地方我们对多个服务进行同步调用,我们阻塞并等待。当同步调用产生瓶颈时,这种方法会增加我们拥有的线程数。
据说,如果我们将这些调用切换到异步请求,我们就可以摆脱瓶颈,因为线程可以继续,并且回调将处理任何需要发生的事情。
据我所知,在Java中,为了进行异步调用,我们生成一个新线程,进行网络调用并包含回调(我不会实现这个,我假设这就是一些Java http库的工作方式。)
所以我的问题是,这如何解决许多线程的问题?异步请求最终会创建更多线程(每个请求一个),然后进入睡眠状态直到返回某些内容,这不会创建许多睡眠线程吗?
我试图解决的问题是,在某些时候,当线程太多时,JVM会爆炸。
答案 0 :(得分:1)
特别是在Web服务/ servlet环境中:
在最简单的配置中,常见的Web服务器(Jetty,Tomcat)配置有固定数量的线程或线程数范围。如果有多个请求到达而不是有线程,那么这些请求将堆积在内核连接队列中。线程接受连接并完成所有工作。发送响应时,该线程可用于另一个连接。添加自己的线程池或执行程序服务无济于事。
在更复杂的配置中,Web容器接受一个线程池上的连接,然后在另一个线程池上调度工作,其间有一个队列。然后,不是在连接时阻止客户端,或者让它们无法连接,而是等待。
在异步Servlet处理中,例如JAX-RS @suspended AsyncResponse
对象,您可以自己控制它的详细信息。 servlet使用包含连接的数据结构调用您。您的代码可以将该对象放入某个队列(可能只是构建在Executor Service中的队列),然后返回。这使Web服务器线程可以接受另一个容器。您的线程(可能来自Executor Service)可以处理队列,处理请求和发送响应。
你永远不会创造无限数量的线程。
答案 1 :(得分:1)
异步意味着请求由另一个线程处理。它不必是专用线程,更不用说新线程了。
例如,考虑JAX-RS asynchronous client callbacks:
target().path("http://example.com/resource/")
.request().async().get(new InvocationCallback<String>() {
@Override
public void completed(String dataFromBackendServer) {
respondWith(dataFromBackendServer);
}
@Override
public void failed(Throwable throwable) {
respondWithError(throwable);
}
});
这里,InvocationCallback在JAX-RS实现提供的线程中执行,该线程等待对任何挂起的后端请求的响应,然后使用适当的InvocationCallback处理该响应。因为单个线程可以等待任意数量的挂起后端请求,所以需要更少的线程。
也就是说,同步处理通常更容易实现,虽然它的扩展性不如异步处理,但它可以适用于许多应用程序。也就是说,除非你有数千个并发请求,否则普通的旧同步处理模型就可以了。
答案 2 :(得分:0)
没有像线程太多的问题,服务器总是有一个线程池。它们将线程池中的线程分配给每个请求,如果线程不可用,则服务器只是使请求套接字在ServerSocket的队列中等待。
Servlet 3中的异步请求处理试图解决的问题是由于阻塞请求处理线程而导致资源利用率降低。
因此,如果有长时间运行的请求等待I / O,它们将被保持,直到从I / O通道收到响应,并且该线程被分配给在套接字队列中等待的另一个请求。
这为我们提供了更好的资源(主要是CPU)利用率,并且随着每秒提供更多请求(持续时间短的请求),可以提供更多的资源。