Future.get() 有没有可以在等待结果的时候不堵塞当前线程?

2019-05-28 18:31:55 +08:00
 aoscici2000

@RestController
public class TestController {

	@Resource(name = "taskExecutor")
	private ThreadPoolTaskExecutor executor;

	@GetMapping("/test")
	public Map<String, Object> test() throws Exception{
    
		Map<String, Object> map = new HashMap<>();

		DemoTask task = new DemoTask();
        
		Future<String> result = executor.submit(task);

		map.put("result", result.get());
        
		return map;
	}
}

class DemoTask implements Callable<String> {

	@Override
    public String call() throws Exception {
		Thread.sleep(5000);
		return "这是要的结果";
	}

}

如果两个人几乎同时访问 /test 的时候, 怎么样可以让这两个请求同时处理, 而不需要先等第一个处理完? 假设是需要等待返回结果的.

3315 次点击
所在节点    Java
22 条回复
Narcissu5
2019-05-28 19:15:54 +08:00
pursuer
2019-05-28 19:16:20 +08:00
不想 Future.get()阻塞就在 task 最后加个回调吧
cyhulk
2019-05-28 19:21:03 +08:00
你可以考虑 spring 的异步相应,直接返回 callable,servelet 3.1 规范支持
zrc
2019-05-28 19:31:58 +08:00
1. 两个人同时访问 /test 不应该就是两个线程吗( springmvc 处理),除非你的逻辑里面要限制只能一定数量的线程同时访问(信号量)
2. 如果只是为了子线程不影响主线程的执行,并且子线程的结果不需要给客户端返回,那主线程不需要进行 get 操作,如果需要进行返回那等主线程执行到必须要子线程结果的时候,那不是必须要阻塞到子线程的数据吗,否则是没有意义的吧
3. 如果是多个子线程,主线程要等多个都返回,那么可以使用 countdownlatch
micean
2019-05-28 19:34:52 +08:00
class DemoTask implements Runnable{
final HttpServletResponse response;

// 省略构造函数


@Override
public void run() {
String 结果 = ...;
response.getWriter().write(结果)
}

}
HuHui
2019-05-28 19:38:48 +08:00
你这个需求 Spring Cloud 中的请求合并了解下
godoway
2019-05-28 21:57:20 +08:00
看看 CompletableFuture 是不是你想要的
godoway
2019-05-28 22:07:55 +08:00
CompletableFuture + DeferredResult
创建一个 DeferredResult,然后在 CompletableFuture 的回调里面 setResult,最后返回 DeferredResult
dengtongcai
2019-05-28 22:39:15 +08:00
CompletableFuture
beidounanxizi
2019-05-28 23:35:09 +08:00
可以用闭锁 还是栅栏 实现 ?随口一说 2 个都用来控制多线程何时处理的
aoscici2000
2019-05-29 11:57:36 +08:00
@godoway 好像还是不行, 都是早早就返回了空结果.
nekoneko
2019-05-29 15:49:05 +08:00
看一下,CountDownLatch 或者 CyclicBarrier 吧
aoscici2000
2019-05-29 22:14:48 +08:00
@godoway 这个能说得详细一点不, 这两货看了一天, 单独分开看好像挺多例子都能看得懂一点, 但结合起来就蒙了...尤其结合到例子里的实际需求的时候...
godoway
2019-05-29 23:20:18 +08:00
@aoscici2000 把 tomcat 的 max-threads 设置为 1,看看日志是不是你想要的那种

```
@GetMapping("defer")
fun testDeferred(): DeferredResult<String> {
log.info("handle request")
val defer = DeferredResult<String>()
CompletableFuture.runAsync {
Thread.sleep(1000)
log.info("async task")
defer.setResult("Hello defer")
}
log.info("defer result")
return defer
}
```
aoscici2000
2019-05-30 01:13:49 +08:00
@godoway 依然还是回到了那个老问题, 只要我需要拿到最终结果, handler 还是得排队来.
```java

@GetMapping("/task")
public DeferredResult<String> ast() {

DeferredResult<String> defer = new DeferredResult<>();

CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
Thread.sleep(5000);
defer.setResult("结果");
});

return defer;
}

```
好像有点理解不来,,,
godoway
2019-05-30 07:45:12 +08:00
@aoscici2000 max-threads=1 意味着只有一个线程处理请求,那么同时来 2 个请求的时候,如果使用你之前用的 future 就会阻塞了这唯一一条线程,结果就是 2 个请求合计使用了 10 秒。但使用 CompletableFuture + DeferredResult 时,2 个请求进来了,第一个请求直接返回了 defer 随意该线程已经完成了第一个请求了,接下来就会直接处理第二个请求。而 CompletableFuture 则是在另外一个线程池执行,当他完成时就返回给用户,于是这时的结果是两个请求合计使用 5 秒多一点时间(取决于你 CompletableFuture 的线程池大小,默认是 CPU-1,如果=1 时,也是需要 10 秒了)
aoscici2000
2019-05-30 10:55:09 +08:00
@godoway 运行结果看起来似乎是每个线程不同, 但等结果返回时还是得一个个来
```java

@Resource(name = "taskExecutor")
private ThreadPoolTaskExecutor executor;

@GetMapping("/async-task")
public DeferredResult<String> ast() {

System.out.println("handler 调用, 当前线程: %s, 当前时间: %s");

DeferredResult<String> defer = new DeferredResult<>();

CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
Thread.sleep(5000);
defer.setResult("这是结果, 当前线程: %s, 当前时间: %s");
System.out.println("这是结果, 当前线程: %s, 当前时间: %s");
}, executor);

return defer;
}

```

1 秒内两次请求 /async-task

handler 调用, 当前线程: 35, 当前时间: 41:07:277
handler 调用, 当前线程: 42, 当前时间: 41:12:460

这是结果, 当前线程: 50, 当前时间: 41:12:304
这是结果, 当前线程: 51, 当前时间: 41:17:470
godoway
2019-05-30 11:48:17 +08:00
@aoscici2000 看起来你的 35 和 42 就相差了 5 秒,确定你的测试用例正确么。
aoscici2000
2019-05-30 12:25:56 +08:00
@godoway 我就简单装着两个人同时访问, 打开两个 localhost/async-task 页面 1 秒内各自刷新了一下, 我想要达到的效果是第二个应该 41:13 就处理了, 41:18 就出结果了
godoway
2019-05-30 14:56:07 +08:00
@aoscici2000
![](//i.imgur.com/L6S5laj.png)
这是 tomcat 只开一个线程,另外一个 worker 线程是 10 个的情况,
由日志可以看出,同时提交 5 个请求,异步的操作是 5 秒后才返回 response。
并不存在你描述的问题,所以你确认你的测试是正确的吗?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/568499

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX