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


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.