今後の注意点
- forループがFutureの結果をバッチで取得する場合、ブロックするのは簡単です。getメソッドを呼び出すときは、タイムアウト制限を使用する必要があります。
Futuresの場合、最初の注意点は、forループがFuturesの結果をバッチで取得するときにブロックしやすいことです。getメソッドを呼び出すときは、タイムアウトを使用して制限する必要があります。
この状況が何であるかを見てみましょう。
まず、実行するタスクが全部で4つあるとすると、それらをスレッドプールに入れ、get()メソッドを実行して取得する1から4の順序で取得します。コードは次のとおりです。次のように:
public class FutureDemo {
public static void main(String[] args) {
//创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//提交任务,并用 Future 接收返回结果
ArrayList<Future> allFutures = new ArrayList<>();
for (int i = 0; i < 4; i++) {
Future<String> future;
if (i == 0 || i == 1) {
future = service.submit(new SlowTask());
} else {
future = service.submit(new FastTask());
}
allFutures.add(future);
}
for (int i = 0; i < 4; i++) {
Future<String> future = allFutures.get(i);
try {
String result = future.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
service.shutdown();
}
static class SlowTask implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(5000);
return "速度慢的任务";
}
}
static class FastTask implements Callable<String> {
@Override
public String call() throws Exception {
return "速度快的任务";
}
}
}
コードでは、新しいスレッドプールを作成し、リストを使用して4つのFutureを格納していることがわかります。その中で、最初の2つのFutureに対応するタスクは遅いタスク、つまりコードの下のSlowTaskであり、最後の2つのFutureに対応するタスクは速いタスクです。遅いタスクは実行時に5秒で完了しますが、速いタスクは非常に高速に実行でき、ほとんど時間がかかりません。
これらの4つのタスクを送信した後、forループを使用してgetメソッドを実行し、実行結果を取得して、結果を出力します。
実行結果は以下のとおりです。
速度慢的任务
速度慢的任务
速度快的任务
速度快的任务
ご覧のとおり、この実行の結果は4行のステートメントを出力し、最初の2つは低速タスクであり、後の2つは高速タスクです。結果は正しいですが、実際には実行中に5秒間待機してから、これらの4行のステートメントを非常に高速に出力します。
ここで問題があります。つまり、3番目のタスクの量が比較的少なく、結果をすばやく返すことができ、4番目のタスクもすぐに結果を返します。ただし、最初の2つのタスクは非常に遅いため、getメソッドを使用して実行すると、最初のタスクでスタックします。つまり、この時点では3番目と4番目のタスクの結果は非常に早い段階で取得されますが、これをforループメソッドを使用して結果を取得すると、3番目と4番目のタスクを時間内に取得できません。タスクの結果。最初のタスクの結果は5秒後まで取得できません。その後、2番目のタスクの結果を取得できます。次に、3番目と4番目のタスクの順番になります。
ネットワーク上の理由により、最初のタスクが最大1分間結果を返すことができない場合があると仮定すると、この時点では、メインスレッドがスタックしたままになり、プログラムの効率に影響します。
この時点で、Futureのget(long timeout、TimeUnit unit)メソッドをタイムアウトパラメーターとともに使用して、この問題を解決できます。このメソッドの機能は、限られた時間内に結果が返されない場合、TimeoutExceptionがスローされ、例外をキャッチまたは再度スローできるため、常にスタックするとは限りません。
2.未来のライフサイクルを後退させることはできません
Futureのライフサイクルを元に戻すことはできません。タスクが完了すると、「完了」状態で永続的に停止し、最初から再開することも、計算を完了したFutureがタスクを再実行することもできません。
これはスレッドとスレッドプールの状態と同じであり、スレッドとスレッドプールの状態を回帰することはできません。スレッドの状態と流路については、図に示すように、前の3で説明しました。
忘れてしまった方は、振り返ってもう一度見てください!
Futureは新しいスレッドを生成しますか?
最後に、この質問にもう一度答えましょう。Futureは新しいスレッドを生成しますか?
Threadクラスの継承とRunnableインターフェースの実装に加えて、新しいスレッドを生成する3番目の方法、つまりCallableとFutureを使用する方法があるということわざがあります。これは、戻り値を持つスレッドを作成する方法と呼ばれます。 。このステートメントは正しくありません。
実際、CallableとFutureは、それ自体で新しいスレッドを生成することはできません。タスクを実行するには、Threadクラスやスレッドプールなどの他のスレッドを使用する必要があります。たとえば、Callableがスレッドプールに送信された後、実際にCallableを実行するのはスレッドプール内のスレッドであり、スレッドプール内のスレッドはThreadFactoryによって生成されます。ここで生成された新しいスレッドは何の関係もありません。 CallableとFutureを使用するため、Future新しいスレッドは生成されません。
Futureに関する2つの注意点:まず、取得時にタイムアウト制限を使用する必要があります。次に、Futureのライフサイクルを回帰できません。次に、CallableとFutureは、実際には新しいスレッドを作成する3番目の方法ではないと言われています。