提问人:Urb 提问时间:10/6/2023 最后编辑:ProgmanUrb 更新时间:10/6/2023 访问量:198
用于从 HTTP 响应读取 InputStream 的虚拟线程
Virtual thread for reading InputStream from HTTP response
问:
使用 java 21,只需在虚拟线程中执行即可将阻塞 IO 代码转换为非阻塞 IO 代码。
我应该简单地包装返回的 HTTP 调用(如在方法中),还是在虚拟线程中执行读取和反序列化(如在方法中)会更有效?InputStream
nonBlockingA
InputStream
nonBlockingB
换言之,读取是否阻塞 IO 操作?InputStream
请记住,响应可能非常大,可能包含超过 500,000 个字符串。 我也不确定使用的库是否使用任何 ThreadLocals,不建议使用虚拟线程
@SuppressWarnings("unchecked")
class Request {
private final ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();
private CloseableHttpClient httpApacheClient;
List<String> nonBlockingA() throws Exception {
InputStream bigInputStream = executorService.submit(this::getResponse).get();
return deserialize(bigInputStream);
}
List<String> nonBlockingB() throws Exception {
return executorService.submit(() -> {
InputStream bigInputStream = getResponse();
return deserialize(bigInputStream);
}).get();
}
private InputStream getResponse() throws IOException {
return httpApacheClient.execute(new HttpGet("http://random/names/size/500000")).getEntity().getContent();
}
private static List<String> deserialize(InputStream body) throws IOException {
try (InputStreamReader reader = new InputStreamReader(body, UTF_8)) {
return new Gson().fromJson(reader, List.class);
}
}
}
答:
您的变体之间没有显着差异。任何返回完全构造结果的方法(如 )始终是同步或阻塞方法,因为无法等待异步或外部计算的任何所需结果来构造最终结果。List<String>
异步方法必须返回一种或类似类型的 promise 对象,这允许它们在计算实际结果之前返回。这要求调用方能够处理这种结果。当调用方必须将完全构造的结果返回给调用方时,它会将您带回原点,因为它再次需要阻塞等待。Future
因此,这种传统的异步处理要求所有处理步骤都实现为异步操作,链接回调以将实际计算推迟到输入可用时。
虚拟线程的要点在于,它们根本不需要您编写这种传统的异步方法。您可以以同步方式编写操作。为了保持你的例子,
List<String> straightForward() throws IOException {
try(InputStream bigInputStream = getResponse()) {
return deserialize(bigInputStream);
}
}
调用方有责任在虚拟线程中调用您的代码,以获得好处。或者,也许最好说调用者可以选择使用您的方法,只是同步或在虚拟线程中使用。但是,调用方也有可能以简单的同步方式使用您的方法,但调用方的调用方在虚拟线程中安排了调用。
在最好的情况下,99% 的代码不处理线程或异步 API,而是以同步方式直接编写。只有剩下的 1% 必须在虚拟线程中安排调用,例如 Web 服务器为每个请求创建一个虚拟线程或类似的东西。
评论
get()
CompletableFuture