2017-03-20 83 views
0

我想了解的Java异步的好处。爪哇 - 异步 - 线程池

方案1: 我有部署到Tomcat的弹簧引导web应用程序,使用Tomcat min和max线程都设定在200

@Service 
public class MyService{ 

    public String execute(){ 
     try { 
      //simulate blocking 
      Thread.sleep(3000); 
     } catch (InterruptedException e) {} 
     return "OK";  
    } 
} 

@RestController 
public class MyController { 

    @Autowired 
    private MyService service; 

    @RequestMapping("/test") 
    public String test(){ 
     return service.execute(); 
    } 
} 

方案2:我有部署到Tomcat的弹簧引导web应用程序与Tomcat的最小和最大线程均设定为100

@Service 
public class MyService{ 

    public String execute(){ 
     try { 
      //simulate blocking 
      Thread.sleep(3000); 
     } catch (InterruptedException e) {} 
     return "OK";  
    } 
} 

@RestController 
public class MyController { 

    @Autowired 
    private MyService service; 

    private ExecutorService executorService = Executors.newFixedThreadPool(100); 

    @RequestMapping("/test") 
    public DeferredResult<String> test(){ 
     DeferredResult<String> deferredResult = new DeferredResult<>(); 
     CompletableFuture.supplyAsync(service::execute, executorService). 
      whenCompleteAsync((result, throwable) -> deferredResult.setResult(result));  
     return deferredResult;  
    } 
} 

在每个场景中,线程的总数为200

但我不明白如何风光ario 2会有更好的表现:

在场景1中,如果有400个请求同时进入,前200个将由200个http线程提供服务,接下来的200个将等待3秒(再加上一点),直到其中一个线程再次可用。

所以可以通过为每6秒400个请求=每秒66.6请求。

的平均响应时间是(200 * 3 + 200 * 6)/(400)=4.5秒

在方案2中,如果 400个请求在同一时间进入。前100个将由100个http线程立即提供服务,这些线程中的每一个都会调用该服务,而不是等待结果,然后立即恢复,并可用于接下来的100个请求。 但现在用于第二100个请求,当每个HTTP线程调用服务,该服务目前等待3秒(减去一个位)来完成处理前100个线程。所以下一个100队列(在executorservice的线程池中)。 因此,几乎没有任何时间,我们处理了所有400个请求,但 100正在服务中处理(等待3秒钟),而300个正在执行程序服务线程池中排队。 3秒后,前100个完成,接下来100个出队并处理,等等。

所以吞吐量是在12秒内400个请求=每秒

的平均响应时间是 (100 * 3 + 100 * 6 + 100 * 9 + 100 * 12)/(400)= 7.5 33.3请求秒

现在,有人可能会说'我可以通过增加执行程序服务线程池中的线程数来改进方案2',我可以回复'好的,然后我可以增加tomcat中的线程数在场景1中的池数量相同'

回答

0

要在此场景中查看异步的优点,您需要mak e您的服务也是异步的。三秒钟后完成运行计划后,它会立即返回,而不是执行关联该线程的睡眠。在这种情况下,所有请求将在三秒钟内完成;吞吐量为每秒133次,平均响应时间为3秒。如果您调整线程数量,您将拥有基本相同的响应时间。

异步点在于你的空闲I/O等待的线程可以立即自由地做其他事情,所以你不必使用很多线程,这是一个昂贵的资源,可以满足你的工作量。

+0

感谢您的回复。 但是,如果服务也是异步并立即返回,哪个线程正在等待完成可完成未来的响应 –

+0

API的调用者仍在等待“OK”响应。有些线程必须返回该响应,即使它不是tomcat http线程。 –

+0

@ jonathan.stiles - 当计时器任务的时间耗尽时,任务被分配给线程池上的空闲线程,或者如果没有空闲线程,则将其分配给下一个线程中服务的队列游泳池变得闲置。线程池中的这个线程最终完成DeferredResult并将响应发送给客户端。在适当的异步中,所有线程都要么完成工作,要么在等待调度的线程池中处于空闲状态。 – antlersoft

0

在你的问题中你有一个非常综合的情况。

比方说,你们两个都有10个线程(10个HTTP线程和异步版本的5 + 5个线程),并且你的程序不仅仅是调用睡眠方法。但是,80%的请求确实需要3秒的操作(比方说数据库查询)。

现在,在这两种情况下,您已经设法同时获得所有线程来调用阻塞方法。到目前为止,没有太大的区别。如果另一个呼叫进入阻塞方法,它将不得不等待。

现在,突然间,您得到了一个不同操作的请求,比如说登录。登录很简单,只需检查数据库中的一行。在第一种情况下,它必须等待3秒钟,因为没有可用的HTTP线程来处理它。在第二种情况下,你有一个完全不相关的线程池,但由于你没有使用它来登录,所以你的登录请求会立即被服务。

好的,为什么不使用DeferredResult创建一个1000线程池?线程很昂贵。你不希望遇到你已经设法得到1000个执行一些昂贵任务的线程的情况,你的CPU是100%,而不是3秒,每个请求的运行时间变为30秒。您的服务器扼流圈和吞吐量将变为0.

这也适用于连接池。少即是多。

+0

我认为这可能不是一个公平的比较,''两个'10线程' 如果每个都有10个http线程,但方案2也有一个executorservice来处理异步服务,那么方案2实际上有更多的线程。 –

+0

没关系。一个对所有请求都有X个通用线程,另一个对于特定的长时间运行(其中Y + Z = X)有Y个通用线程和Z个线程。第二个允许更好的吞吐量。你的例子太糟糕了(并且完全不基于现实),它让你感到困惑。优点来自*不是所有的请求都是异步的,而是为那些需要花费大量时间等待的人自己管理。 – Kayaman