繁体   English   中英

Java - 异步 - 线程池

[英]Java - Async - Thread pool

我试图了解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池中的线程数相同金额'

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

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

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

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM