저는 일반적인 C # 개발자이지만 때로는 Java로 응용 프로그램을 개발합니다. C # async / await에 해당하는 Java가 있는지 궁금합니다. 간단히 말하면 Java는 다음과 같습니다.
async Task<int> AccessTheWebAsync()
{
HttpClient client = new HttpClient();
var urlContents = await client.GetStringAsync("http://msdn.microsoft.com");
return urlContents.Length;
}
답변
아니요, Java 또는 v5 이전의 C #에는 async / await에 해당하는 것이 없습니다.
배후에 상태 머신을 구축하는 것은 상당히 복잡한 언어 기능입니다.
Java에서 비동기 / 동시성에 대한 언어 지원은 상대적으로 거의 없지만 java.util.concurrent
패키지에는 이와 관련된 유용한 클래스 가 많이 있습니다 . (작업 병렬 라이브러리와 동일하지는 않지만 가장 가까운 근사값입니다.)
답변
이 await
때 비동기 동작이 완료 (추가의 코드를 실행하는 계속 사용 client.GetStringAsync(...)
).
따라서 가장 가까운 근사값으로 CompletableFuture<T>
(Java 8 .net Task<TResult>
) 기반 솔루션을 사용하여 Http 요청을 비동기 적으로 처리합니다.
2016 년 5 월 25 일에 Abril 13 일에 릴리스 된 AsyncHttpClient v.2로 업데이트 :
따라서 OP 예제와 동등한 Java 8 AccessTheWebAsync()
은 다음과 같습니다.
CompletableFuture<Integer> AccessTheWebAsync()
{
AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient();
return asyncHttpClient
.prepareGet("http://msdn.microsoft.com")
.execute()
.toCompletableFuture()
.thenApply(Response::getResponseBody)
.thenApply(String::length);
}
이 사용법은 비동기 HTTP 클라이언트 요청에서 CompletableFuture를 얻는 방법에 대한 답변에서 가져 왔습니다.
이는 2016 년 Abril 13 일에 릴리스 된 AsyncHttpClient 버전 2에서 제공되는 새로운 API에 따라 이미 본질적으로 지원됩니다 CompletableFuture<T>
.
AsyncHttpClient 버전 1을 사용한 원본 답변 :
이를 위해 두 가지 가능한 접근 방식이 있습니다.
-
첫 번째는 비 차단 IO를 사용하고 그것을 호출합니다
AccessTheWebAsyncNio
. 그러나AsyncCompletionHandler
함수형 인터페이스가 아닌 추상 클래스 이기 때문에 람다를 인수로 전달할 수 없습니다. 따라서 익명 클래스의 구문으로 인해 불가피한 상세한 표현이 발생합니다. 그러나이 솔루션은 주어진 C # 예제의 실행 흐름에 가장 가깝습니다 . -
두 번째는 약간 덜 장황하지만 응답이 완료 될 때까지 스레드를 궁극적으로 차단하는 새 작업 을 제출합니다
f.get()
.
첫 번째 방법 은 더 장황하지만 비 차단입니다.
static CompletableFuture<Integer> AccessTheWebAsyncNio(){
final AsyncHttpClient asyncHttpClient = new AsyncHttpClient();
final CompletableFuture<Integer> promise = new CompletableFuture<>();
asyncHttpClient
.prepareGet("https://msdn.microsoft.com")
.execute(new AsyncCompletionHandler<Response>(){
@Override
public Response onCompleted(Response resp) throws Exception {
promise.complete(resp.getResponseBody().length());
return resp;
}
});
return promise;
}
두 번째 접근법은 덜 장황하지만 스레드를 차단합니다.
static CompletableFuture<Integer> AccessTheWebAsync(){
try(AsyncHttpClient asyncHttpClient = new AsyncHttpClient()){
Future<Response> f = asyncHttpClient
.prepareGet("https://msdn.microsoft.com")
.execute();
return CompletableFuture.supplyAsync(
() -> return f.join().getResponseBody().length());
}
}
답변
Java 바이트 코드를 다시 작성하여 비동기 / 대기를 시뮬레이션하는 ea-async 를 확인하십시오 . 읽어보기 당 : “.NET CLR의 Async-Await에서 많은 영향을 받았습니다”
답변
async 와 await 는 구문 설탕입니다. 비동기 및 대기의 본질은 상태 머신입니다. 컴파일러는 async / await 코드를 상태 머신으로 변환합니다.
동시에 실제 프로젝트에서 async / await를 실제로 구현하려면 이미 많은 비동기 I / O 라이브러리 함수 가 있어야합니다 . C #의 경우 대부분의 원래 동기화 된 I / O 기능에는 대체 비동기 버전이 있습니다. 이러한 비동기 함수가 필요한 이유는 대부분의 경우 자체 비동기 / 대기 코드가 일부 라이브러리 비동기 메소드로 요약되기 때문입니다.
C #의 비동기 버전 라이브러리 함수는 Java의 AsynchronousChannel 개념과 비슷합니다. 예를 들어, 읽기 작업이 완료된 후 Future를 반환하거나 콜백을 실행할 수있는 AsynchronousFileChannel.read가 있습니다. 그러나 정확히 동일하지는 않습니다. 모든 C # 비동기 함수는 작업을 반환합니다 (미래와 비슷하지만 미래보다 더 강력 함).
Java가 async / await를 지원한다고 가정하면 다음과 같은 코드를 작성합니다.
public static async Future<Byte> readFirstByteAsync(String filePath) {
Path path = Paths.get(filePath);
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path);
ByteBuffer buffer = ByteBuffer.allocate(100_000);
await channel.read(buffer, 0, buffer, this);
return buffer.get(0);
}
그런 다음 컴파일러가 원래 async / await 코드를 다음과 같이 변환한다고 상상할 수 있습니다.
public static Future<Byte> readFirstByteAsync(String filePath) {
CompletableFuture<Byte> result = new CompletableFuture<Byte>();
AsyncHandler ah = new AsyncHandler(result, filePath);
ah.completed(null, null);
return result;
}
다음은 AsyncHandler의 구현입니다.
class AsyncHandler implements CompletionHandler<Integer, ByteBuffer>
{
CompletableFuture<Byte> future;
int state;
String filePath;
public AsyncHandler(CompletableFuture<Byte> future, String filePath)
{
this.future = future;
this.state = 0;
this.filePath = filePath;
}
@Override
public void completed(Integer arg0, ByteBuffer arg1) {
try {
if (state == 0) {
state = 1;
Path path = Paths.get(filePath);
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path);
ByteBuffer buffer = ByteBuffer.allocate(100_000);
channel.read(buffer, 0, buffer, this);
return;
} else {
Byte ret = arg1.get(0);
future.complete(ret);
}
} catch (Exception e) {
future.completeExceptionally(e);
}
}
@Override
public void failed(Throwable arg0, ByteBuffer arg1) {
future.completeExceptionally(arg0);
}
}
답변
언어 수준의 Java에서는 C # async / await에 해당하지 않습니다. 로 알려진 개념 섬유는 일명 협력 스레드 일명 가벼운 스레드 흥미로운 대안이 될 수있다. 파이버에 대한 지원을 제공하는 Java 라이브러리를 찾을 수 있습니다.
파이버를 구현하는 Java 라이브러리
섬유에 대한 훌륭한 소개를 위해이 기사 (Quasar에서) 를 읽을 수 있습니다 . 여기에는 스레드가 무엇인지, JVM에서 파이버를 구현하는 방법 및 Quasar 관련 코드가 있습니다.
답변
언급했듯이 직접 동등한 것은 없지만 Java 바이트 코드 수정 (비동기 / 대기 유사 명령어 및 기본 연속 구현 모두)을 사용하여 매우 가까운 근사치를 만들 수 있습니다.
JavaFlow 연속 라이브러리 위에 async / await를 구현하는 프로젝트에서 지금 작업하고 있습니다. https://github.com/vsilaev/java-async-await 를 확인 하십시오.
Maven mojo가 아직 작성되지 않았지만 제공된 Java 에이전트로 예제를 실행할 수 있습니다. async / await 코드는 다음과 같습니다.
public class AsyncAwaitNioFileChannelDemo {
public static void main(final String[] argv) throws Exception {
...
final AsyncAwaitNioFileChannelDemo demo = new AsyncAwaitNioFileChannelDemo();
final CompletionStage<String> result = demo.processFile("./.project");
System.out.println("Returned to caller " + LocalTime.now());
...
}
public @async CompletionStage<String> processFile(final String fileName) throws IOException {
final Path path = Paths.get(new File(fileName).toURI());
try (
final AsyncFileChannel file = new AsyncFileChannel(
path, Collections.singleton(StandardOpenOption.READ), null
);
final FileLock lock = await(file.lockAll(true))
) {
System.out.println("In process, shared lock: " + lock);
final ByteBuffer buffer = ByteBuffer.allocateDirect((int)file.size());
await( file.read(buffer, 0L) );
System.out.println("In process, bytes read: " + buffer);
buffer.rewind();
final String result = processBytes(buffer);
return asyncResult(result);
} catch (final IOException ex) {
ex.printStackTrace(System.out);
throw ex;
}
}
@async는 메소드를 비동기 실행 파일로 플래그 지정하는 주석이고, await ()는 연속을 사용하여 CompletableFuture를 대기하는 함수이며 “return asyncResult (someValue)”호출은 연관된 CompletableFuture / Continuation을 마무리하는 것입니다
C #과 마찬가지로 제어 흐름이 유지되고 예외 처리가 규칙적인 방식으로 수행 될 수 있습니다 (시퀀스 실행 코드와 같은 시도 / 캐치)