programing tip

C # async / await와 동일한 Java?

itbloger 2020. 7. 7. 07:54
반응형

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 itself has no equivalent features, but third-party libraries exist which offer similar functionality, e.g.Kilim.


There isn't anything native to java that lets you do this like async/await keywords, but what you can do if you really want to is use a CountDownLatch. You could then imitate async/await by passing this around (at least in Java7). This is a common practice in Android unit testing where we have to make an async call (usually a runnable posted by a handler), and then await for the result (count down).

Using this however inside your application as opposed to your test is NOT what I am recommending. That would be extremely shoddy as CountDownLatch depends on you effectively counting down the right number of times and in the right places.


First, understand what async/await is. It is a way for a single-threaded GUI application or an efficient server to run multiple "fibers" or "co-routines" or "lightweight threads" on a single thread.

If you are ok with using ordinary threads, then the Java equivalent is ExecutorService.submit and Future.get. This will block until the task completes, and return the result. Meanwhile, other threads can do work.

If you want the benefit of something like fibers, you need support in the container (I mean in the GUI event loop or in the server HTTP request handler), or be writing your own. For example, Servlet 3.0 offers asynchronous processing. JavaFX offers javafx.concurrent.Task. These don't have the elegance of language features, though. They work through ordinary callbacks.


I make and released Java async/await library. https://github.com/stofu1234/kamaitachi

This library don't need compiler extension, and realize stackless IO processing in Java.

    async Task<int> AccessTheWebAsync(){ 
        HttpClient client= new HttpClient();
        var urlContents= await client.GetStringAsync("http://msdn.microsoft.com");
       return urlContents.Length;
    }

   ↓

    //LikeWebApplicationTester.java
    BlockingQueue<Integer> AccessTheWebAsync() {
       HttpClient client = new HttpClient();
       return awaiter.await(
            () -> client.GetStringAsync("http://msdn.microsoft.com"),
            urlContents -> {
                return urlContents.length();
            });
    }
    public void doget(){
        BlockingQueue<Integer> lengthQueue=AccessTheWebAsync();
        awaiter.awaitVoid(()->lengthQueue.take(),
            length->{
                System.out.println("Length:"+length);
            }
            );
    }

Java has unfortunately no equivalent of async/await. The closest you can get is probably with ListenableFuture from Guava and listener chaining, but it would be still very cumbersome to write for cases involving multiple asynchronous calls, as the nesting level would very quickly grow.

If you're ok with using a different language on top of JVM, fortunately there is async/await in Scala which is a direct C# async/await equivalent with an almost identical syntax and semantics: https://github.com/scala/async/

Note that although this functionality needed a pretty advanced compiler support in C#, in Scala it could be added as a library thanks to a very powerful macro system in Scala and therefore can be added even to older versions of Scala like 2.10. Additionally Scala is class-compatible with Java, so you can write the async code in Scala and then call it from Java.

There is also another similar project called Akka Dataflow http://doc.akka.io/docs/akka/2.3-M1/scala/dataflow.html which uses different wording but conceptually is very similar, however implemented using delimited continuations, not macros (so it works with even older Scala versions like 2.9).


If you're just after clean code which simulates the same effect as async/await in java and don't mind blocking the thread it is called on until it is finished, such as in a test, you could use something like this code:

interface Async {
    void run(Runnable handler);
}

static void await(Async async) throws InterruptedException {

    final CountDownLatch countDownLatch = new CountDownLatch(1);
    async.run(new Runnable() {

        @Override
        public void run() {
            countDownLatch.countDown();
        }
    });
    countDownLatch.await(YOUR_TIMEOUT_VALUE_IN_SECONDS, TimeUnit.SECONDS);
}

    await(new Async() {
        @Override
        public void run(final Runnable handler) {
            yourAsyncMethod(new CompletionHandler() {

                @Override
                public void completion() {
                    handler.run();
                }
            });
        }
    });

AsynHelper Java library includes a set of utility classes/methods for such asynchronous calls (and wait).

If it is desired to run a set of method calls or code blocks asynchronously, the It includes an useful helper method AsyncTask.submitTasks as in below snippet.

AsyncTask.submitTasks(
    () -> getMethodParam1(arg1, arg2),
    () -> getMethodParam2(arg2, arg3)
    () -> getMethodParam3(arg3, arg4),
    () -> {
             //Some other code to run asynchronously
          }
    );

If it is desired to wait till all asynchronous codes are completed running, the AsyncTask.submitTasksAndWait varient can be used.

Also if it is desired to obtain a return value from each of the asynchronous method call or code block, the AsyncSupplier.submitSuppliers can be used so that the result can be then obtained by from the result suppliers array returned by the method. Below is the sample snippet:

Supplier<Object>[] resultSuppliers = 
   AsyncSupplier.submitSuppliers(
     () -> getMethodParam1(arg1, arg2),
     () -> getMethodParam2(arg3, arg4),
     () -> getMethodParam3(arg5, arg6)
   );

Object a = resultSuppliers[0].get();
Object b = resultSuppliers[1].get();
Object c = resultSuppliers[2].get();

myBigMethod(a,b,c);

If the return type of each method differ, use the below kind of snippet.

Supplier<String> aResultSupplier = AsyncSupplier.submitSupplier(() -> getMethodParam1(arg1, arg2));
Supplier<Integer> bResultSupplier = AsyncSupplier.submitSupplier(() -> getMethodParam2(arg3, arg4));
Supplier<Object> cResultSupplier = AsyncSupplier.submitSupplier(() -> getMethodParam3(arg5, arg6));

myBigMethod(aResultSupplier.get(), bResultSupplier.get(), cResultSupplier.get());

The result of the asynchronous method calls/code blocks can also be obtained at a different point of code in the same thread or a different thread as in the below snippet.

AsyncSupplier.submitSupplierForSingleAccess(() -> getMethodParam1(arg1, arg2), "a");
AsyncSupplier.submitSupplierForSingleAccess(() -> getMethodParam2(arg3, arg4), "b");
AsyncSupplier.submitSupplierForSingleAccess(() -> getMethodParam3(arg5, arg6), "c");


//Following can be in the same thread or a different thread
Optional<String> aResult = AsyncSupplier.waitAndGetFromSupplier(String.class, "a");
Optional<Integer> bResult = AsyncSupplier.waitAndGetFromSupplier(Integer.class, "b");
Optional<Object> cResult = AsyncSupplier.waitAndGetFromSupplier(Object.class, "c");

 myBigMethod(aResult.get(),bResult.get(),cResult.get());

Java doesn't have direct equivalent of C# language feature called async/await, however there's a different approach to the problem that async/await tries to solve. It's called project Loom, which will provide fibers (lightweight user-mode threads) for high-throughput lightweight concurrency. It will be available in some future version of OpenJDK.

This approach also solves "colored function problem" that async/await has.

Similar feature can be also found in Golang (goroutines).

참고URL : https://stackoverflow.com/questions/16539245/java-equivalent-of-c-sharp-async-await

반응형