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


126

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


1
J-Go libのコードをもう一度表示できますか?
msangel 2013

私はストリームを文字ごとに読んでいるプロジェクトに取り組んでいました。完全な単語が読み込まれると、その単語に対して多くの操作を実行していました。最後にコレクションに入れました。ストリームからすべてのデータが読み取られたら、応答を返します。単語を操作するたびにスレッドを開始することにしました。ただし、全体的なパフォーマンスは低下しました。次に、スレッド自体が高価な操作であることを知りました。スレッドを開始してメソッドを同時に呼び出すことで、重いIO操作を実行するまで、パフォーマンスが向上するかどうかはわかりません。
Amit Kumar Gupta

2
すばらしい質問です!! Java 8は、このためにCompletableFuturesを提供します。その他の回答は、おそらく古いバージョンのJavaに基づいています
プログラマ

回答:


155

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

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

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

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

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


あなたの答えが私の問題を助けました-stackoverflow.com/questions/27009448/…。私の状況を適用するのは少しトリッキーですが、最終的にはうまく
いきました

パラメータで呼び出す方法は?
yatanadam 16

2
@yatanadamこれはあなたの質問に答えるかもしれません。上記のコードをメソッド内に配置し、通常のように変数をパスします。私が作成したこのテストコードを確認してください。
user3004449 2017

1
@eNnillaMS実行後にスレッドを停止する必要がありますか?自動的に停止しますか、ガベージコレクターによって停止しますか?
user3004449 2017

@ user3004449スレッドは自動的に停止しますが、強制することもできます。
ショーン

58

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

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

CompletableFuture別の答えで言及されましたが、それはsupplyAsync(...)チェーン全体を使用しました。これは、質問に完全に適合する単純なラッパーです。
ndm13 2018

ForkJoinPool.commonPool().execute()オーバーヘッドがわずかに少ない
マークジェロニ

31

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

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

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

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


問題は、1つのメソッド呼び出しを実行することだけです。この方法では、ターゲットクラスの実装を変更する必要があります。私が欲しかったのは、RunnableやCallableへの影響を心配することなく、正確に電話をかけることです
Felipe Hummel

私はあなたを聞く。Javaは(まだ)ファーストクラスの機能を持っていないので、これは現在のところ最先端の技術です。
shadit 2009

'future'キーワードに感謝します...現在、それらに関するチュートリアルを開いています...非常に便利です。:D
gumuruh

4
-1私が知る限り、FutureTaskだけでは、非同期で何かを実行するには不十分です。それを実行するには、カルロスの例のように、スレッドまたはエグゼキュータを作成する必要があります。
MikeFHay 2013

24

そのためにReflectionを使用するという考えは好きではありません。
いくつかのリファクタリングで欠落しているだけでなく、によって拒否される可能性もありますSecurityManager

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

    Executors.newSingleThreadExecutor().submit(task);

少しスレッドを作成するよりも少し短い(タスクが呼び出し可能かRunnableをあります)


1
問題は、1つのメソッド呼び出しを実行することだけです。この方法では、ターゲットクラスの実装を変更する必要があります。私が欲しかったのは、RunnableやCallableの実装について心配することなく正確に呼び出すことです
Felipe Hummel

このwould'ntヘルプはずっと:(しかし、通常、私は反射の代わりにRunnableをまたはコーラブルを使用して好むだろう
user85421

4
短いメモ:ExecutorServiceインスタンスを追跡する方がいいので、必要なshutdown()ときに呼び出すことができます。
Daniel Szalay

1
これを行った場合、クローズされていないExecutorServiceが発生し、JVMがシャットダウンを拒否する可能性がありますか?シングルスレッドの、時間制限のあるExecutorServiceを取得するための独自のメソッドを作成することをお勧めします。
djangofan 2017

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
このアプローチでは、saveを呼び出すためのすべての呼び出しが非同期になることに注意してください。特定のメソッドの一部の呼び出しのみを非同期にする必要がある場合は、このページで提案されている他の方法を使用できます。
bmorin

Webアプリケーションで使用できますか(スレッドの管理は推奨されていないため)?
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
Spring-Bootを使用している場合。
Giovanni Toraldo、2015年

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

これは、この使用してJava 8.使用することが本当にうれしいです
ムクティ

3

CactoosAsyncFuncから使用できます。

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

これは、バックグラウンドで実行され、結果がで利用できるようになりますget()ようにFuture


2

これは実際には関係ありませんが、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();
}

私はこれをテストしましたが、うまくいきます。彼らが「非同期の方法」のために来ただけなら、それが他の人を助けるかもしれないと思っただけです。


1

EAによって作成されたAsync-Await用の素敵なライブラリもありますhttps : //github.com/electronicarts/ea-async

彼らのReadmeから:

EA非同期

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非同期なし

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.