자바에서 비동기 적으로 메서드를 호출하는 방법


127

최근 에 Go의 고 루틴을 살펴 보았고 Java에서 비슷한 것을 갖는 것이 좋을 것이라고 생각했습니다. 메서드 호출을 병렬화하는 일반적인 방법을 검색 한 한 다음과 같은 작업을 수행합니다.

final String x = "somethingelse";
new Thread(new Runnable() {
           public void run() {
                x.matches("something");             
    }
}).start();

그다지 우아하지 않습니다. 이 작업을 수행하는 더 좋은 방법이 있습니까? 프로젝트에서 이러한 솔루션이 필요했기 때문에 비동기 메서드 호출을 중심으로 자체 래퍼 클래스를 구현하기로 결정했습니다.

J-Go 에 래퍼 클래스를 게시했습니다 . 그러나 그것이 좋은 해결책인지 모르겠습니다. 사용법은 간단합니다.

SampleClass obj = ...
FutureResult<Integer> res = ...
Go go = new Go(obj);
go.callLater(res, "intReturningMethod", 10);         //10 is a Integer method parameter
//... Do something else
//...
System.out.println("Result: "+res.get());           //Blocks until intReturningMethod returns

또는 덜 장황함 :

Go.with(obj).callLater("myRandomMethod");
//... Go away
if (Go.lastResult().isReady())                //Blocks until myRandomMethod has ended
    System.out.println("Method is finished!");

내부적으로 저는 Runnable을 구현하는 클래스를 사용하고 있으며 올바른 메서드 개체를 가져 와서 호출하기 위해 몇 가지 Reflection 작업을 수행합니다.

내 작은 라이브러리와 Java에서 이와 같은 비동기 메서드 호출을 만드는 주제에 대한 의견이 필요합니다. 안전 해요? 이미 더 간단한 방법이 있습니까?


1
J-Go lib의 코드를 다시 보여줄 수 있습니까?
msangel

나는 현명한 스트림을 읽는 프로젝트에서 일하고 있었다. 완전한 단어가 읽 히면 그 단어에 대해 많은 작업을 수행했습니다. 마지막으로 컬렉션에 넣었습니다. 스트림의 모든 데이터를 읽으면 응답을 반환합니다. 단어에 대한 작업을 수행 할 때마다 스레드를 시작하기로 결정했습니다. 그러나 전반적인 성능이 저하되었습니다. 그런 다음 스레드 자체가 비용이 많이 드는 작업이라는 것을 알게되었습니다. 메서드를 동시에 호출하기 위해 스레드를 시작하면 무거운 IO 작업을 수행 할 때까지 성능을 추가 할 수 있는지 확실하지 않습니다.
Amit Kumar Gupta

2
좋은 질문 !! Java 8은이를 위해 CompletableFutures를 제공합니다. 다른 답변은 아마도 이전 버전의 Java를 기반으로합니다
TriCore

답변:


155

나는 당신을 할 수있는 더 깨끗한 방법이 있음을 방금 발견했습니다.

new Thread(new Runnable() {
    public void run() {
        //Do whatever
    }
}).start();

(적어도 Java 8에서는) 람다 식을 사용하여 다음과 같이 줄일 수 있습니다.

new Thread(() -> {
    //Do whatever
}).start();

JS에서 함수를 만드는 것처럼 간단합니다!


귀하의 답변은 내 문제에 도움이되었습니다 -stackoverflow.com/questions/27009448/… . 까다로운 리틀 :) 내 situatioin을 적용하지만, 결국 그것을 밖으로 작동합니다
데커드

매개 변수로 호출하는 방법?
yatanadam

2
@yatanadam 이것은 귀하의 질문에 답할 수 있습니다. 위의 코드를 메소드 안에 넣고 평소처럼 변수를 지정하면됩니다. 내가 만든 이 테스트 코드를 확인하세요 .
user3004449

1
@eNnillaMS 실행 후 스레드를 중지해야합니까? 자동으로 중지됩니까, 아니면 가비지 수집기에 의해 중지됩니까 ?
user3004449 apr

@ user3004449 스레드가 자동으로 중지되지만 강제로 실행할 수도 있습니다.
Shawn

59

Java 8은 java.util.concurrent.CompletableFuture 패키지에서 사용할 수있는 CompletableFuture를 도입했으며 비동기 호출을 만드는 데 사용할 수 있습니다.

CompletableFuture.runAsync(() -> {
    // method call or code to be asynch.
});

CompletableFuture다른 답변에서 언급되었지만 그 하나는 전체 supplyAsync(...)체인을 사용했습니다 . 이것은 질문에 완벽하게 맞는 간단한 래퍼입니다.
ndm13

ForkJoinPool.commonPool().execute()약간 적은 오버 헤드가
마크 Jeronimus

31

수업을 고려할 수도 있습니다 java.util.concurrent.FutureTask.

Java 5 이상을 사용하는 경우 FutureTask 는 "취소 가능한 비동기 계산"의 턴키 구현입니다.

java.util.concurrent패키지 에서 사용할 수있는 더 풍부한 비동기 실행 스케줄링 동작 (예 :)이 ScheduledExecutorService있지만 FutureTask필요한 모든 기능이있을 수 있습니다.

심지어 사용 FutureTask가능해진 이후로 예제로 제공 한 첫 번째 코드 패턴을 더 이상 사용하지 않는 것이 좋습니다 . (Java 5 이상을 사용한다고 가정합니다.)


문제는 하나의 메서드 호출을 실행하고 싶습니다. 이렇게하면 대상 클래스의 구현을 변경해야합니다. 내가 원하는 것은의 Runnable 또는 Callable를 구현하는 방법에 대한 걱정없이 정확하게 호출입니다
펠리페 험멜

들려요. 자바에는 (아직) 일류 기능이 없기 때문에 이것이 바로 지금의 최첨단 기능입니다.
shadit 2009

'미래'키워드에 감사드립니다 ... 이제 그에 대한 튜토리얼을 시작하겠습니다 ... 매우 유용합니다. : D
gumuruh

4
-1 내가 말할 수있는 한, FutureTask는 그 자체로는 비동기 적으로 실행하기에 충분하지 않습니다. Carlos의 예에서와 같이 스레드 또는 실행기를 실행하려면 여전히 작성해야합니다.
MikeFHay

24

나는 그것을 위해 Reflection을 사용하는 아이디어가 마음에 들지 않습니다.
일부 리팩토링에서 누락되어 위험 할뿐만 아니라 SecurityManager.

FutureTaskjava.util.concurrent 패키지의 다른 옵션과 마찬가지로 좋은 옵션입니다.
내가 가장 좋아하는 간단한 작업 :

    Executors.newSingleThreadExecutor().submit(task);

조금 (작업이 호출 가능 또는의 Runnable입니다) 스레드를 만드는 것보다 짧은 비트


1
문제는 하나의 메서드 호출을 실행하고 싶습니다. 이렇게하면 대상 클래스의 구현을 변경해야합니다. 내가 원하는 것은의 Runnable 또는 Callable를 구현하는 방법에 대한 걱정없이 정확하게 호출입니다
펠리페 험멜

그런 다음 이것은 많이 도움이되지 않을 것입니다 :(하지만 일반적으로 리플렉션 대신 Runnable 또는 Callable을 사용하는 것을 선호합니다
user85421

4
짧은 메모 : ExecutorService인스턴스를 추적하는 것이 더 좋으므로 shutdown()필요할 때 전화 를 걸 수 있습니다.
다니엘 Szalay

1
이렇게하면 닫히지 않은 ExecutorService로 끝나서 JVM이 종료를 거부하지 않습니까? 단일 스레드, 시간 제한이있는 ExecutorService를 얻기 위해 고유 한 방법을 작성하는 것이 좋습니다.
djangofan 2017-08-22

14

CompletableFuture에 Java8 구문을 사용할 수 있습니다. 이렇게하면 비동기 함수 호출의 결과를 기반으로 추가 비동기 계산을 수행 할 수 있습니다.

예를 들면 :

 CompletableFuture.supplyAsync(this::findSomeData)
                     .thenApply(this:: intReturningMethod)
                     .thenAccept(this::notify);

자세한 내용은이 기사 에서 찾을 수 있습니다.


10

jcabi-aspects 및 AspectJ의 @Async주석을 사용할 수 있습니다 .

public class Foo {
  @Async
  public void save() {
    // to be executed in the background
  }
}

를 호출 save()하면 새 스레드가 시작되고 본문을 실행합니다. 의 결과를 기다리지 않고 주 스레드가 계속됩니다 save().


1
이 접근 방식은 모든 호출을 비동기식으로 저장하며, 이는 우리가 원하는 것일 수도 있고 아닐 수도 있습니다. 특정 메서드에 대한 일부 호출 만 비 동기화해야하는 경우이 페이지에서 제안하는 다른 접근 방식을 사용할 수 있습니다.
bmorin

웹 응용 프로그램에서 사용할 수 있습니까 (스레드 관리가 권장되지 않기 때문에)?
onlinenaman

8

이를 위해 Future-AsyncResult를 사용할 수 있습니다.

@Async
public Future<Page> findPage(String page) throws InterruptedException {
    System.out.println("Looking up " + page);
    Page results = restTemplate.getForObject("http://graph.facebook.com/" + page, Page.class);
    Thread.sleep(1000L);
    return new AsyncResult<Page>(results);
}

참조 : https://spring.io/guides/gs/async-method/


10
스프링 부트를 사용하는 경우.
Giovanni Toraldo

6

Java는 또한 비동기 메서드를 호출하는 좋은 방법을 제공합니다. 에 있는 java.util.concurrent 우리는이 ExecutorService에 같은 일을하는 데 도움이됩니다. 다음과 같이 개체를 초기화하십시오.

 private ExecutorService asyncExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

그런 다음 다음과 같이 함수를 호출하십시오.

asyncExecutor.execute(() -> {

                        TimeUnit.SECONDS.sleep(3L);}

3

실제 솔루션은 아닐 수 있지만 이제 Java 8에서는 람다 식을 사용하여이 코드를 조금 더보기 좋게 만들 수 있습니다.

final String x = "somethingelse";
new Thread(() -> {
        x.matches("something");             
    }
).start();

그리고이 작업을 한 줄로도 할 수 있으며 여전히 읽을 수 있습니다.

new Thread(() -> x.matches("something")).start();

그것은이 사용하는 자바 8. 사용하는 것이 정말 좋다
Mukti

3

CactoosAsyncFunc 에서 사용할 수 있습니다 .

boolean matches = new AsyncFunc(
  x -> x.matches("something")
).apply("The text").get();

그것은 배경에서 실행되고 결과에서 사용할 수 get()A와 Future.


2

이것은 실제로 관련이 없지만 match ()와 같은 메서드를 비동기 적으로 호출하려면 다음을 사용합니다.

private final static ExecutorService service = Executors.newFixedThreadPool(10);
public static Future<Boolean> matches(final String x, final String y) {
    return service.submit(new Callable<Boolean>() {

        @Override
        public Boolean call() throws Exception {
            return x.matches(y);
        }

    });
}

그런 다음 비동기 메서드를 호출하려면 다음을 사용합니다.

String x = "somethingelse";
try {
    System.out.println("Matches: "+matches(x, "something").get());
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}

나는 이것을 테스트했고 작동합니다. "비동기 방법"을 위해 온 경우 다른 사람들에게 도움이 될 것이라고 생각했습니다.


1

EA에서 만든 Async-Await 용 멋진 라이브러리도 있습니다 : https://github.com/electronicarts/ea-async

읽어보기에서 :

EA Async 사용

import static com.ea.async.Async.await;
import static java.util.concurrent.CompletableFuture.completedFuture;

public class Store
{
    public CompletableFuture<Boolean> buyItem(String itemTypeId, int cost)
    {
        if(!await(bank.decrement(cost))) {
            return completedFuture(false);
        }
        await(inventory.giveItem(itemTypeId));
        return completedFuture(true);
    }
}

EA Async없이

import static java.util.concurrent.CompletableFuture.completedFuture;

public class Store
{
    public CompletableFuture<Boolean> buyItem(String itemTypeId, int cost)
    {
        return bank.decrement(cost)
            .thenCompose(result -> {
                if(!result) {
                    return completedFuture(false);
                }
                return inventory.giveItem(itemTypeId).thenApply(res -> true);
            });
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.