[java] C # async / await와 동일한 Java?

저는 일반적인 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에서 많은 영향을 받았습니다”


답변

asyncawait 는 구문 설탕입니다. 비동기 및 대기의 본질은 상태 머신입니다. 컴파일러는 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 #과 마찬가지로 제어 흐름이 유지되고 예외 처리가 규칙적인 방식으로 수행 될 수 있습니다 (시퀀스 실행 코드와 같은 시도 / 캐치)


답변

Java 자체에는 동등한 기능이 없지만 Kilim 과 같은 유사한 기능을 제공하는 타사 라이브러리가 있습니다 .