【问题标题】:Java: Does making async calls increase number of threads?Java:进行异步调用会增加线程数吗?
【发布时间】:2016-01-08 20:23:05
【问题描述】:

假设我有一个网络应用程序,对于每个请求,我们都会生成一个新线程。数百个请求进来,在 Web 服务器代码的某个地方,我们对多个服务进行同步调用,我们阻塞并等待。这种方法会使我们拥有的线程数量膨胀,因为同步调用会造成瓶颈。

假设,如果我们将这些调用切换为异步请求,我们将摆脱瓶颈,因为线程可以继续,回调将处理任何需要发生的事情。

据我所知,在 Java 中,为了进行异步调用,我们会生成一个新线程来进行网络调用并包含回调(我不会实现这个,我假设这就是Java http 库工作)。

所以我的问题是,这如何解决多线程的问题?异步请求最终会创建更多线程(每个请求一个),然后进入睡眠状态,直到返回某些内容,这不会创建许多睡眠线程吗?

我要解决的问题是,在某些时候,当线程过多时,JVM 会爆炸。

【问题讨论】:

  • 这是发生了还是你在谈论一些理论上的事情?
  • 每次收到请求时都不需要生成一个新线程。通常,这是由线程池解决方案处理的。

标签: java multithreading asynchronous


【解决方案1】:

特别是在 web 服务/servlet 环境中:

在最简单的配置中,常见的 Web 服务器(Jetty、Tomcat)配置有固定数量的线程或线程数量范围。如果到达的请求多于线程数,那么这些请求将堆积在内核连接队列中。线程接受连接并完成所有工作。发送响应后,该线程可用于另一个连接。添加自己的线程池或执行器服务无济于事。

在更复杂的配置中,Web 容器在一个线程池上接受连接,然后在另一个线程池上分派工作,中间有一个队列。然后,它们不会在连接时阻止客户端,或者让它们无法连接,而是等待。

在异步 Servlet 处理中,例如 JAX-RS @suspended AsyncResponse 对象,您可以自己控制这些细节。 servlet 使用包含连接的数据结构调用您。您的代码可以将该对象放入某个队列(可能只是内置于 Executor Service 中的队列),然后返回。这释放了 Web 服务器线程以接受另一个容器。您的线程(可能来自 Executor Service)通过队列工作,处理请求并发送响应。

您永远不会创建无限数量的线程。

【讨论】:

  • 我的问题是关于请求到达后服务执行的阻塞/非阻塞调用。这不会创建更多线程吗?非阻塞调用如何在幕后工作?
  • 您必须更加具体。 Java 中几乎没有任何 API 会“创建更多线程”。
【解决方案2】:

异步表示请求由另一个线程处理。它不必是专用线程,更不用说线程了。

例如,考虑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 处理该响应。因为单个线程可以等待任意数量的待处理后端请求,所以需要更少的线程。

也就是说,同步处理通常更容易实现,虽然它的扩展性不如异步处理,但它的扩展性足以满足许多应用程序。也就是说,除非您有数千个并发请求,否则普通的旧同步处理模型就可以了。

【讨论】:

    【解决方案3】:

    没有线程太多的问题,服务器总是有一个线程池。他们从线程池中为每个请求分配一个线程,如果一个线程不可用,服务器只是让请求套接字在 ServerSocket 的队列中等待。

    Servlet 3 中的异步请求处理试图解决的问题是由于请求处理线程阻塞而导致的资源利用率较低。

    因此,如果有长时间运行的请求只是等待 I/O,它们将被搁置,直到从 I/O 通道接收到响应,并且该线程被分配给在套接字队列中等待的另一个请求。

    这为我们提供了更好的资源(主要是 CPU)利用率,并随着每秒处理更多请求(持续时间短的请求)而提供更多资源。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多