web-dev-qa-db-ja.com

Javaのメソッドを非同期的に呼び出す方法

私は Goのgoroutines を最近見ていましたが、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でこのような非同期メソッド呼び出しを行うというテーマについて意見を求めています。安全ですか?簡単な方法はすでにありますか?

97
Felipe Hummel

私はあなたをするよりクリーンな方法があることを発見しました

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

(少なくともJava 8では)、ラムダ式を使用して次のように短縮できます。

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

JSで関数を作成するのと同じくらい簡単!

137
AegisHexad

Java 8は、パッケージJava.util.concurrent.CompletableFutureで利用可能なCompletableFutureを導入し、非同期呼び出しを行うために使用できます。

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

クラスJava.util.concurrent.FutureTaskも検討することをお勧めします。

Java 5以降を使用している場合、 FutureTask は「キャンセル可能な非同期計算」のターンキー実装です。

Java.util.concurrentパッケージ(たとえば、ScheduledExecutorService)には、さらに豊富な非同期実行スケジューリング動作がありますが、FutureTaskには必要な機能がすべて備わっている場合があります。

FutureTaskが利用可能になって以来、例として挙げた最初のコードパターンを使用することはもはやお勧めできません。 (Java 5以降にいると仮定します。)

31
shadit

そのためにReflectionを使用するという考えは好きではありません。
一部のリファクタリングで見落とされる危険があるだけでなく、SecurityManagerによって拒否されることもあります。

FutureTaskは、Java.util.concurrentパッケージの他のオプションと同様の優れたオプションです。
単純なタスクで私のお気に入り:

    Executors.newSingleThreadExecutor().submit(task);

littleスレッドの作成よりも少し短い(タスクは呼び出し可能または実行可能)

23

CompletableFutureにはJava8構文を使用できます。これにより、非同期関数を呼び出した結果に基づいて追加の非同期計算を実行できます。

例えば:

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

詳細はこちらをご覧ください 記事

11
Tal Avissar

@Async アノテーションを jcabi-aspects およびAspectJから使用できます:

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

save()を呼び出すと、新しいスレッドが開始され、その本体が実行されます。メインスレッドは、save()の結果を待たずに続行します。

10
yegor256

これには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/

7
user3177227

これはおそらく本当の解決策ではありませんが、今では-Java 8で-ラムダ式を使用して、このコードを少なくとも少し良く見せることができます。

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

そして、これを1行で実行することもできますが、それでもかなり読みやすくなっています。

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

AsyncFunc from Cactoos を使用できます。

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

バックグラウンドで実行され、結果はget()Futureとして利用可能になります。

2
yegor256

Javaは、非同期メソッドを呼び出す素晴らしい方法も提供します。 Java.util.concurrentExecutorServiceを使用して同じことを実行できます。このようにオブジェクトを初期化します-

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

そして、次のような関数を呼び出します

asyncExecutor.execute(() -> {

                        TimeUnit.SECONDS.sleep(3L);}
2
Anand

これは実際には関連していませんが、メソッドを非同期に呼び出す場合はmatches()、私は使用します:

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();
}

私はこれをテストしましたが、動作します。彼らが「非同期メソッド」のために来た場合、それは他の人を助けるかもしれないと思った。

2
FlameBlazer