JAvaベースの同時実行

3650 ワード

また、concurrentパッケージが登場する前は、私たちにとって本当に厄介な問題でしたが、jdkの機能が強化されるにつれて、これらの問題はずいぶん簡略化されました.では、concurrentパッケージはいったい何なのか見てみましょう.
このパッケージがあれば、実行されたタスクだけを作成し、スレッドプールに投げ込みます.それらの間の並列は心配しなくてもいいです.
concurrentパッケージには、次のようなスレッドプールがいくつかあります.
   newFixedThreadPool(int nThreads)
固定スレッド数を再利用可能なスレッドプールを作成し、共有された無境界キューでスレッドを実行します.任意の点で、ほとんどのnThreadsスレッドはタスクを処理するアクティブな状態にあります.すべてのスレッドがアクティブなときに追加タスクをコミットすると、使用可能なスレッドがある前に追加タスクがキュー内で待機します.クローズ前の実行中に失敗してスレッドが終了した場合、必要に応じて新しいスレッドが後続のタスクを実行します.スレッドが明示的に閉じるまで、プール内のスレッドは常に存在します.
   newSingleThreadExecutor()
単一のworkerスレッドを使用するExecutorを作成し、スレッドを無境界キューで実行します.(クローズ前の実行中に失敗したために単一のスレッドが終了した場合、必要に応じて新しいスレッドが後続のタスクを実行します).各タスクを順次実行し、任意の所定の時間に複数のスレッドがアクティブでないことを保証することができる.他の等価なnewFixedThreadPool(1)とは異なり、このメソッドが返す実行プログラムを再構成することなく、他のスレッドを使用できることを保証します.
    newCachedThreadPool()
必要に応じて新しいスレッドを作成できるスレッドプールを作成しますが、以前に構築したスレッドが使用可能になった場合に再利用されます.これらのスレッドプールは、多くの短期非同期タスクを実行するプログラムの場合、通常、プログラムのパフォーマンスを向上させることができます.executeを呼び出すと、以前に構築したスレッドが再利用されます(スレッドが使用可能な場合).既存のスレッドが使用できない場合は、新しいスレッドを作成してプールに追加します.60秒も使用されていないスレッドをキャッシュから削除します.したがって、長時間空きを維持しているスレッドプールでは、リソースは使用されません.ただし、ThreadPoolExecutor構築メソッドを使用して、タイムアウトパラメータなどの詳細が異なるプロパティを持つスレッドプールを作成できます.
   newScheduledThreadPool()
指定された遅延後にコマンドを実行したり、定期的に実行したりするスレッドプールを作成します.
これらはすべてExecutorsで作成されたもので、返されるのはすべてExecutorServiceで、このExecutorServiceが主役になり始め、ExecutorServiceはコミットされたRunnableタスクのオブジェクトを実行することができます.このインタフェースは、スレッドの使用の詳細、スケジューリングなど、各タスクがどのように実行されるかを含むメカニズムからタスクコミットを分離する方法を提供します.スレッドは通常、明示的に作成するのではなくExecutorを使用します.例えば、一連のタスクの各々に対してnew Thread(new(RunnableTask()を呼び出すのではなく、以下の方法を用いることができる.start():管理終了の方法と、1つ以上の非同期タスクの実行状況を追跡するためにFutureを生成する方法も提供されます.
public interface ExecutorService extends Executor {

   
    void shutdown();

    List<Runnable> shutdownNow();

   
    boolean isShutdown();

    boolean isTerminated();

    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;

    <T> Future<T> submit(Callable<T> task);

    <T> Future<T> submit(Runnable task, T result);

   
    Future<?> submit(Runnable task);

    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;

   
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit)
        throws InterruptedException;

    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;

    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

これはExecutorServiceの主な方法で、その一般的な操作手順は
1)Executorsでスレッドプールを作成し、
2)ExecutorServiceのsubmit()メソッドを実行し、値を返す必要がある場合は3)を実行します.
3)submit()メソッドの戻り値をFutureクラスに割り当てます.このクラスはRunnable機能に与えたのと同じですが、違いはFutureに戻り値があります.Futureのget()メソッドを呼び出して戻り値を取得すればOKです.
注意:ExecutorServiceのsubmitリロードメソッドにはRunableパラメータがあります.このRunnableが実装したタスクは返す価値がありません.このメソッドが正常に返された場合、nullが返されます.
なぜこの方法を設計したのですか?個人的には複数のタスクをコミットする場合、タスクはRunnable、callableタイプで、コミットされる必要があり、Runnableタスクをコミットできないと実行が中断し、後のタスクは何も実行されず、他のタスクはまだ考えられていないと思います.
授業の後で考える
問題1
このような設計モードの長所と短所は?
問題2
もしあなただったら、この同時問題をどのように設計すればいいですか?