[Java Concurrency Tools-Collaboration] Futureはどのようにマルチスレッドを使用して最適な「ボイルティー」プログラムを実現するのですか?

前回の記事では、スレッドプールを正しく作成する方法、タスクを送信するためのThreadPoolExecutor execute(実行可能タスク)のメソッドを紹介しましたが、以下では実行タスクの戻り結果について説明します。

1.タスクの実行結果を取得するにはどうすればよいですか?

Javaは、ThreadPoolExecutorによって提供される3つのsubmit()メソッドとFutureTaskツールクラスを使用して、タスクの実行結果を取得します。

1.1 3つのsubmit()メソッド

// 1. 提交Runnable任务
Future<?> submit(Runnable task);
// 2.提交Callable任务
<T> Future<T>  submit(Callable<T> task);
// 3.提交Runnable任务及结果引用  
<T> Future<T>   submit(Runnable task, T result);

3つのsubmit()メソッドの戻り値は、5つのメソッドを持つFutureインターフェースであることがわかります。

// 取消任务
boolean cancel(  boolean mayInterruptIfRunning);
// 判断任务是否已取消  
boolean isCancelled();
// 判断任务是否已结束
boolean isDone();
// 阻塞式获得任务执行结果,如果线程任务还没执行结束,那么调用get()的线程会被阻塞,直到任务执行结束才会唤醒
get();
// 阻塞式获得任务执行结果,支持超时
get(long timeout, TimeUnit unit);

3つの送信方法の違いはパラメーターが異なるため、それらについて個別に説明しましょう。

  1. メソッドパラメータはRunnableであり、Runnableインターフェイスのrun()メソッドには戻り値がないため、このFutureはThread.join()と同様にタスクの終了のみをアサートできます。
  2. Callableインターフェースのcall()メソッドには戻り値があるため、Futureインターフェースのget()メソッドを呼び出して、タスクの実行結果を取得できます。
  3. 最後に、パラメータを送信する3番目の方法はRunableとresultです。Runableには戻り値はありませんが、Futureインターフェースを呼び出すget()メソッドは結果の結果を返します。
    結果は、メインスレッドとサブスレッド間のブリッジに相当します。これにより、メインサブスレッドがデータを共有できます。
    次に、3番目の送信メソッドの古典的な使用方法を示します:Runnableインターフェースの実装クラスは、パラメーター化されたコンストラクターTask(Result r)を宣言します。Taskオブジェクトが作成されると、結果オブジェクトが渡され、Taskクラスで実行できます。 ()メソッドの結果に対してさまざまな操作が実行されました。
ExecutorService executor   = Executors.newFixedThreadPool(1);
// 创建Result对象r
Result r = new Result();
r.setAAA(a);
// 提交任务
Future<Result> future =   executor.submit(new Task(r), r);  
Result fr = future.get();
// 下面等式成立
fr === r;
fr.getAAA() === a;
fr.getXXX() === x

class Task implements Runnable{
  Result r;
  //通过构造函数传入result
  Task(Result r){
    this.r = r;
  }
  void run() {
    //可以操作result
    a = r.getAAA();
    r.setXXX(x);
  }
}

1.2 submit()メソッドとexecute()メソッドの違いは何ですか?

  1. パラメータは同じではありません。submit()の3番目の送信メソッドは、resultパラメータで渡すことができます。
  2. submit()によって実行されたスレッドは結果Futureを返すことができますが、execute()は実行結果を返すことができません。
  3. 例外処理は同じではなく、submit()メソッドです。Futureのget()メソッドの例外を処理して、スレッドを試行、キャッチ、および実行できます。タスクの実行中に例外が発生すると、スレッドが終了し、例外を処理できなくなります。先に試して、スレッド実行メソッドで例外をキャッチしてください。
//submit方法处理异常
		try {  
              fs.get(); 
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            } catch (ExecutionException e) {  
                executorService.shutdownNow();  
                e.printStackTrace();  
                return;  
            }  
 //execute方法处理异常,只能在Runnable接口或其他的run方法中处理异常。
  threadPoolTaskExecutor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("sleep后");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

1.3 FutureTaskツール

上記のFutureはインターフェースであり、FutureTaskは実際にはツールクラスであり、2つのコンストラクターがあります。

FutureTask(Callable<V> callable);
FutureTask(Runnable runnable, V result);//和上面的一样。

実際、FutureTaskはRunnableインターフェースとFutureインターフェースを実装しています。一方で、それはタスクとしてスレッドに送信して実行できます。また、タスクの実行結果も取得できます。次のサンプルコードを見ることができます:

// 创建FutureTask
FutureTask<Integer> futureTask  = new FutureTask<>(()-> 1+2);
// 创建线程池
ExecutorService es =   Executors.newCachedThreadPool();
// 提交FutureTask 
es.submit(futureTask);
// 获取计算结果
Integer result = futureTask.get();

2.最適な「水を燃やしてお茶を作る」プログラムを実現する

湯を沸かしてお茶を淹れる最適なプロセスは次のようになります:
ここに画像の説明を挿入
同時プログラミングのコア問題:分業、同期、相互排除、最初に行うことは分業、つまりタスクを効率的に分解してスレッドに割り当てる方法です
ここに画像の説明を挿入
もちろん、最善の方法は、ツールクラスCountDownLatch、join()を以前のjavaツールクラスで使用できます。また、Futureを使用して実装できます。

最初に2つのFutureTasks-ft1とft2を作成します。ft1はやかんの洗浄、お湯の沸騰、お茶の製造を終了し、ft2はティーポットの洗浄、ティーカップの洗浄、および茶葉の取り出しを終了します。もちろん、ft1がお茶を淹れる前に、ft2を実行する必要があります。

// 创建任务T2的FutureTask
FutureTask<String> ft2  = new FutureTask<>(new T2Task());
// 创建任务T1的FutureTask
FutureTask<String> ft1  = new FutureTask<>(new T1Task(ft2));
// 线程T1执行任务ft1
Thread T1 = new Thread(ft1);
T1.start();
// 线程T2执行任务ft2
Thread T2 = new Thread(ft2);
T2.start();
// 等待线程T1执行结果
System.out.println(ft1.get());

// T1Task需要执行的任务:
// 洗水壶、烧开水、泡茶
class T1Task implements Callable<String>{
  FutureTask<String> ft2;
  // T1任务需要T2任务的FutureTask
  T1Task(FutureTask<String> ft2){
    this.ft2 = ft2;
  }
  @Override
  String call() throws Exception {
    System.out.println("T1:洗水壶...");
    TimeUnit.SECONDS.sleep(1);
    
    System.out.println("T1:烧开水...");
    TimeUnit.SECONDS.sleep(15);
    // 获取T2线程的茶叶  
    String tf = ft2.get();//拿到ft2的执行结果,没执行结束,阻塞等待(只是拿ft2的执行结果,并不是让ft2任务才执行)
    System.out.println("T1:拿到茶叶:"+tf);

    System.out.println("T1:泡茶...");
    return "上茶:" + tf;
  }
}
// T2Task需要执行的任务:
// 洗茶壶、洗茶杯、拿茶叶
class T2Task implements Callable<String> {
  @Override
  String call() throws Exception {
    System.out.println("T2:洗茶壶...");
    TimeUnit.SECONDS.sleep(1);

    System.out.println("T2:洗茶杯...");
    TimeUnit.SECONDS.sleep(2);

    System.out.println("T2:拿茶叶...");
    TimeUnit.SECONDS.sleep(1);
    return "龙井";
  }
}
// 一次执行结果:
T1:洗水壶...
T2:洗茶壶...
T1:烧开水...
T2:洗茶杯...
T2:拿茶叶...
T1:拿到茶叶:龙井
T1:泡茶...
上茶:龙井

詳細:Deng Xin
リファレンス:Geek Time

元の記事を34件公開 Likes0 Visits 1089

おすすめ

転載: blog.csdn.net/qq_42634696/article/details/105527138