Java - 异步 - 线程池

时间:2017-03-20 19:38:17

标签: java multithreading asynchronous

我试图了解java中async的好处。

场景1: 我有一个部署到tomcat的spring boot web app,tomcat min和max threads都设置为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的spring boot web app,tomcat min和max threads都设置为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。

但我不知道情景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个请求=每秒33.3个请求

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

现在,有人可能会争辩说,“我可以通过增加执行程序服务线程池中的线程数来改进方案2”,我可以回复,“好吧,然后我到达增加方案1中tomcat池中的线程数量相同的数量&#39;

2 个答案:

答案 0 :(得分:0)

要在此方案中查看async的优点,您需要使服务也异步。它不是做一个阻塞线程的Sleep,而是在三秒钟后完成调度运行后立即返回。在这种情况下,所有请求将在不到三秒的时间内完成;您的吞吐量为每秒133个请求,平均响应时间为3秒。如果您将线程数量调低,那么您的响应时间基本相同。

异步的要点是,等待I / O空闲的线程可以立即自由地执行其他操作,因此您不必使用尽可能多的线程(这是一种昂贵的资源)来满足您的需求。工作量。

答案 1 :(得分:0)

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

让我们说你们两个都有10个线程(10个HTTP线程和5 + 5个线程用于异步版本),而你的程序不仅仅是调用一个休眠的方法。但是,80%的请求确实涉及3秒钟的操作(让我们说数据库查询)。

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

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

好的,那么为什么不在不使用DeferredResult的情况下创建1000个大小的线程池呢?线程很贵。您不希望遇到这样的情况:您已经设法获得1000个执行某项昂贵任务的线程,您的CPU处于100%而不是3秒,每个请求的运行时间变为30秒。您的服务器阻塞和吞吐量变为0。

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