[予約] Javaはロジック(77)をプログラム - 非同期タスクの実行サービスを

Javaと大幅に私たちは、このフレームワークを探っていき、このセクションでは、非同期タスクを実行するために必要な開発を簡素化するフレームワーク契約を、提供します。

プレゼンテーションの前に、スレッドのスレッドが実行されるタスクの両方を表し、また、メカニズムの実装と言って、「実行サービス」の概念の導入のための枠組みを設定し、それは「ミッションの提出」と「ミッション」の相分離う「実行サービスは、」ジョブの投入が懸念しているため、そのようなスレッドの作成、タスクなどのタスクを実行するために細部への注意を必要とせずに、このようなジョブを送信すると、タスク自体に焦点を当てた結果を取得するには、タスクをキャンセルすることができ、タスクの実行の詳細をカプセル化スケジューリング、スレッドは閉鎖さのように。

上記の説明は、その後、我々は、具体的に対処ステップ、より抽象的かもしれません。

基本的なインターフェイス

まず、基本的なインターフェイスでの外観は、サービスに関連するタスクを実行してみましょう:

  • Runnableを、呼び出し可能:非同期タスクのために実行されます
  • エグゼキュータとExecutorServiceの:そのサービスの実装
  • 未来:結果は非同期タスクです

Runnableを和呼び出し可能

Runnableをとコーラブルについては、我々が学んできたこれまでのセクションでは、我々は仕事を言っている、Runnableを、結果を返しませんが、そこに呼び出し可能、Runnableを、例外をスローしない、と呼び出し可能になります。

エグゼキュータ和ExecutorServiceの

エグゼキュータは、次のように定義された最も簡単な実行サービスを、表します。

パブリックインターフェース執行{ 
    ボイド(Runnableをコマンド)を実行します。
}

それはあなたが何も結果が返されなかったRunnableを実行することができるということです。インタフェースは限定任務ではありませんどのように、新しいスレッドを作成することができます、スレッドは、スレッドプールを多重化することができる、それが呼び出し側のスレッドで実行することができます。

ExecutorServiceのは、より多くのサービスが定義されている、執行を拡張し、基本的な方法は以下のとおりです。

コードをコピー
パブリックインターフェイスExecutorServiceのがエグゼキュータを拡張{ 
    <T>将来<T>提出する(呼び出し可能<T>タスク)。
    <T>の未来<T>(Runnableをタスク、T結果)を提出します。
    <?>フューチャー(Runnableをタスク)を提出。
    // ...其他方法
}
コードをコピー

これらの3つが表明しているの復帰時に、戻り値の型が将来ある、タスクを提出提出しますが、タスクが提出されたと述べ、それが実行されたという意味ではありません、あなたは最終的な結果を得るために、今後による非同期タスクのステータスを確認することができ、キャンセルタスク。私たちは、呼び出し可能なため、タスクは最終的には戻り値を持っている、知っている、とのRunnableのためには戻り値はありません、そして第二の方法は、同時に非同期タスクの終了時に返却、Runnableをに結果を提出して提供することができ、そして第三の方法のため、非同期戻り値の最後のタスクはnullです。

未来

私たちは、未来のインタフェース定義を見てみましょう。

コードをコピー
パブリックインターフェース将来<V> { 
    (ブールmayInterruptIfRunning)をキャンセルBOOLEAN。
    ブールisCancelled()。
    ブールisDone()。
    VのGET()は例外:InterruptedException、ExecutionExceptionをスローします。
    VのGET(long timeout、TimeUnit unit)指定InterruptedExceptionある、スロー

        ExecutionException、TimeoutExceptionを。
}
コードをコピー

タスクが実行されていない場合は、非同期タスクのリターンのための最終的な結果を得るために待ってブロックする、別の方法を取得し、限られた時間を待ってブロックすることがあり、タイムアウトタスクが完了しない場合、TimeoutExceptionがスローされます。

タスクが完了、またはキャンセル、またはキャンセルすることはできません何らかの理由で、偽を返す、true、そうでない場合にキャンセルされた場合、非同期タスクをキャンセルするキャンセル。タスクがまだ開始されていない場合、それはもはや実行中ではありません。タスクがすでに実行されている場合でも、あなたはmayInterruptIfRunningがtrueの場合はfalseが、それは、我々は69からスレッドを破るしようとしますが、ない場合は、タスクは、スレッド割り込み割り込みメソッドを呼び出すかどうか、実行された場合と言ったパラメータをキャンセルすることができないかもしれませんフェスティバルは、割り込みがスレッドを取り消すことができない場合があり、知っています。

isDoneとisCancelledは、クエリタスクのステータスに使用しました。isCancelledは、スレッドの使命は、本当に終わっていない持っている場合でも、trueの場合、isCancelledメソッドの戻り、タスクはちょうど真のメソッドの戻りをキャンセルし、キャンセルされたかどうかを示します。isDoneはキャンセルされたどんな理由がカウントされているため、タスクの正常終了することができ、タスクは例外がスローされることがあり、それは作業になる可能性があり、タスクが完了したことを示しています。

私たちはgetメソッド、3つの結果について、最終的なタスクを見てみましょう:

  1. 正常に終了し、タスクが実行可能であるとの結果を提供しなかった場合、メソッドの復帰にその実行結果を取得し、リターンはnull
  2. タスクの実行は、再び例外包装ExecutionExceptionをスローするメソッドを取得し、あなたがgetCause珍しい方法で元の異常を取得することができます例外がスローされます
  3. タスクがキャンセルされた、getメソッドは例外をスローしCancellationException

あなたがスレッドのgetメソッドが中断されて呼び出した場合、メソッドは例外:InterruptedExceptionをスローし得ます。

今後の重要なコンセプトは同じ時間コラボレーションで、かつ「ミッション」相分離「タスクを送信」興味のその単離それぞれのポイントを経由して「リンク」の一つ、タスクの提出者とタスク実行サービスで実現するための鍵です。

基本的な使い方

基本的な例

インターフェースは、どのようにそれを使用することを言いましたか?私たちは、簡単な例を見て:

コードをコピー
パブリッククラスBasicDemo { 
    静的クラスのタスクが実装呼び出し可能<整数> { 
        @Override 
        公共整数コール()は例外{スロー
            int型のsleepSecondsを=新しいランダム()nextInt(1000)。
            Thread.sleep(sleepSeconds)。
            sleepSecondsを返します。
        } 
    } 

    公共の静的な無効メイン(文字列[]引数)を{例外:InterruptedExceptionをスロー
        ExecutorServiceのエグゼキュータ= Executors.newSingleThreadExecutor()。
        将来の<整数>未来= executor.submit(新しいタスク()); 

        //模拟执行其他任务
        のThread.sleep(100)。

        試す{ 
            のSystem.out.println(future.get())。 
        }キャッチ(ExecutionException電子){ 
            e.printStackTrace();
        } 
        executor.shutdown()。
    } 
}
コードをコピー

私たちは、ここで使用する、ExecutorServiceのを作成するために使用することができ、タスクの実行サービスを作成し、エグゼキュータは、複数の静的メソッドを持つファクトリクラスエグゼキュータを使用することです:

パブリック静的ExecutorServiceのnewSingleThreadExecutor()

すべてのサービスの実行スレッドを使用することを示し、フォローアップ我々は詳細エグゼキュータは、単数形で執行相との違いに注意します、それはインターフェースです。

ExecutorServiceのがユーザーのために作成されたかに関係なく、使用方法と同じです、タスクの例を提出は、提出後、あなたは他のものに進むことができ、あなたは、最終的な結果や今後によって行われる異常処理タスクを得ることができます。

最後に、我々はExecutorServiceのシャットダウンメソッドを呼び出し、それがタスクの実行サービスを終了します。

ExecutorServiceののより多くの方法

以前、我々はわずか3つのExecutorServiceのメソッドを提出紹介し、実際には、それは次のメソッドがあります。

コードをコピー
パブリックインタフェースExecutorServiceのは、エグゼキュータ{延び
    ボイドシャットダウン(); 
    一覧<Runnableを> shutdownNowの(); 
    ブールisShutdown()。
    ブールisTerminated()。
    ブールawaitTermination(長いタイムアウト、TimeUnit unit)指定
        例外:InterruptedExceptionをスローします。
    <T>一覧<今後の<T >> invokeAll(?コレクション<呼び出し可能<T >>タスクを拡張するには)
        例外:InterruptedExceptionがスローされます。
    <T>一覧<今後の<T >> invokeAll(?コレクション<呼び出し可能<T >>タスク、拡張
                                  long timeout、TimeUnit unit)指定
        例外:InterruptedExceptionがスローされます。
    <T> T invokeAny(コレクション<?呼び出し可能<T >>タスクを拡張)
    <T> 
                    long timeout、TimeUnit unit)指定
        InterruptedExceptionある、ExecutionException、TimeoutExceptionがスローされます。
}
コードをコピー

近くに二つの方法、シャットダウンおよびshutdownNowのがありますが、差はもはや新しいタスクを受け入れているが、タスクが送信されたシャットダウンタスクがまだ開始されていない場合でも、継続します、である、shutdownNowのは、新たな割り当てが提出されまし受け入れないが、まだ実行されていないだけでなく、タスクは、タスクのために終了するスレッドを打破しようとしているの割り込み方法は、一般的な呼び出しを行ったが、スレッドが割り込みに応答しないことがありますされ、shutdownNowの戻りが提出さが、まだタスクリストを実行していないされています。

シャットダウンおよびshutdownNowのは、彼らがすべてのタスクが完了していることを意味しますが、isShutdownメソッドがtrueを返していない返却した後、待機してブロックされません。タイムアウト前にすべてのタスクが終わった場合、呼び出し側は、すべてのタスクの終了についてawaitTermination待ち、それはtrue、そうでない場合はfalseを返す真isTerminatedメソッドが戻ることを、時間を待って定義することができます。

ExecutorServiceのバッチジョブを提出する2つの方法がありますが、invokeAllとinvokeAnyは、彼らが時間を待って定義一つは2つのバージョンを、持っています。

タスクが正常に実行されていることを意味するものではありません完成され、将来のリストが返され、各メソッドのisDone今後はtrueを返しますが、isDoneがtrueのすべてのタスクを待つinvokeAll、タイムアウトが後がある場合invokeAllは、待機時間を指定することができ、キャンセルされた可能性がありますミッションが完了した、それがキャンセルされます。

すべてのタスクは、制限時間内であれば何のタスクが正常に、TimeoutExceptionをスローし、制限時間内に返さない場合invokeAnyについては、限りがあり、制限時間成功したリターン内のタスクがあり、そしてそれは、タスクの結果を返すように、他のタスクがキャンセルされます以上が、例外スローExecutionException受けています。

invokeAllのExecutorServiceの例

私たちは、64 jsoupをダウンロードしてHTMLを用いて分析セクションで紹介し、我々は、invokeAllの例を参照してくださいダウンロードには、2つのURLながら、タイトル、コンテンツ出力、コードのタイトルを分析するためにそれを使用します。

コードをコピー
パブリッククラスInvokeAllDemo { 
    静的クラスUrlTitleParser実装呼び出し可能<文字列> { 
        プライベート文字列のURL。

        公共UrlTitleParser(文字列のURL){ 
            this.url = URL。
        } 

        @Override 
        公共の文字列()の呼び出しは、例外{スロー
            文書DOC = Jsoup.connectを(URL)に.get(); 
            要素要素= doc.select(「ヘッドタイトル」)。
            IF(elements.size()> 0){ 
                戻りelements.get(0)の.text(); 
            } 
            はnullを返します。
        } 
    }
 
    パブリック静的無効メイン(文字列[] args){
        ExecutorServiceのエグゼキュータ= Executors.newFixedThreadPool(10)。
                }キャッチ(ExecutionException電子){
        文字列のURL1 = "http://www.cnblogs.com/swiftma/p/5396551.html"。
        文字列のURL2 = "http://www.cnblogs.com/swiftma/p/5399315.html"。

        コレクション<UrlTitleParser>タスク=は、Arrays.asList(新しいUrlTitleParser [] { 
                新しいUrlTitleParser(URL1)、新しいUrlTitleParser(URL2)})。
        試す{ 
            一覧<今後の<string >> = executor.invokeAll(タスク、10、結果
                    TimeUnit.SECONDS)。
            以下のための(将来の<string>結果:結果){ 
                試み{ 
                    のSystem.out.println(result.get())。
                    e.printStackTrace();
            e.printStackTrace(); 
        } 

        executor.shutdown()。
    } 

}
コードをコピー

ここでは、newFixedThreadPoolの別のファクトリメソッドエグゼキュータは、複数のタスクは、スレッドプール、私たちの次のセクションに、同時に実行できるように、スレッドプールを作成します。

その他のコードは比較的単純である、我々は説明することはできません。使用ExecutorServiceのは、逐次プログラムを書くようなコード書か同時非同期タスクは、スレッドがタスクのみを提出する必要があります作成と調整、気にしない、との結果がすることができ、大幅に開発作業を簡素化します。

基本的な実装原理

基本的な使用のExecutorServiceの未来を学び、我々は彼らの基本的な実装原理を見てください。

ExecutorServiceのメイン実装クラスは、我々がスレッドプールの実装を紹介しますスレッドプール次のセクションに基づいており、ThreadPoolExecutorです。今後の主要な実装クラスはFutureTaskあるExecutorServiceの抽象実装クラスAbstractExecutorServiceを持って、このセクションで、私たちは簡単にその原理を分析し、簡単なExecutorServiceのを実装するために、それに基づいて、我々は簡単に原理を説明します。

AbstractExecutorService

AbstractExecutorServiceは提出の提供、およびinvokeAny invokeAllのデフォルトの実装では、サブクラスの必要性は、次のメソッドを実装します。

コードをコピー
ます。public voidシャットダウン()
公共一覧<Runnableを> shutdownNowの()
isShutdown(ブールパブリック)
isTerminated()boolean型のパブリック
パブリックブールawaitTermination(long timeout、TimeUnit unit)指定
        InterruptedExceptionあるスロー
公共ボイドは(Runnableをコマンド)を実行します
コードをコピー

実行に加えて、実装、簡潔に関連する他の方法およびライフサイクル管理サービスは、我々はその実装を無視し、主な考慮事項は、実行されます。

提出/ invokeAll / invokeAny最終的には次のように私たちは、簡単なExecutorServiceの完全な実装クラスをタスクごとにスレッドを作成し、シンプルさ、タスクを実行する方法を最終的に判定するを実行、実行呼び出します。

コードをコピー
パブリッククラスSimpleExecutorServiceが延びるAbstractExecutorService { 

    @Override 
    公共ボイドシャットダウン(){ 
    } 

    @Override 
    公衆リスト<のRunnable> shutdownNowの(){ 
        戻りヌル。
    } 

    @Override 
    isShutdownブールパブリック(){ 
        戻り偽。
    } 

    @Override 
    isTerminatedブールパブリック(){ 
        戻り偽。
    } 

    @Override 
    パブリックブールawaitTermination(長いタイムアウト、TimeUnit unit)指定
            InterruptedExceptionある{スロー
        リターン偽と、
    } 

    @Override  
    公共ボイドは(Runnableをコマンド)を実行{
        新しいスレッド(コマンド).start();
    } 
}
コードをコピー

前の例では、コードを作成することができExecutorServiceのはに置き換え:

ExecutorServiceのエグゼキュータ=新しいSimpleExecutorService();

あなたは、同じ効果を得ることができます。

ExecutorServiceの最も基本的な方法は、それを達成する方法を、提出するのですか?私たちは、AbstractExecutorServiceコードを見て:

コードをコピー
公共<T>の未来<T>(呼び出し可能<T>タスク)を提出{ 
    場合(タスク== nullの)(新しいNullPointerExceptionがスロー)。
    RunnableFuture <T> ftask = newTaskFor(タスク)。
    (ftask)を実行します。
    ftask返します。
}
コードをコピー

それは、将来のように、RunnableFutureが実行可能を拡張するだけでなく、未来を拡張し、新しいメソッドをRunnableをとして定義されていないだけでなく、インタフェースであり、それは実行の実行メソッドに渡され、実行されるタスクを表す、newTaskForはRunnableFutureを発生呼び出し結果は、非同期タスクの実行を示しています。これは、我々は、特定のコードを見て、混乱することがあります。

保護<T> RunnableFuture <T> newTaskFor(呼び出し可能<T>呼び出し可能){ 
    新しいFutureTaskを返す<T>(呼び出し可能)。
}

オブジェクトはFutureTaskを作成し、FutureTaskはRunnableFutureインタフェースを実現しました。それは、それを達成する方法ですか?

FutureTask

これは、メンバ変数は、実行されるタスクを表した、と宣言:

プライベート呼び出し可能<V>呼び出し可能。

そこ整数変数の状態は、状態を示し、宣言しました:

プライベート揮発int型の状態。

値が指定できます。

コードをコピー
NEW = 0; //状態の始まり、またはタスクが実行されている
補完----------------- = 1; //一時的な状況、ミッションは、近くに描画が結果を設定
//正常に完了したタスクを実行し、NORMAL = 2 
EXCEPTIONAL = 3; //スロータスクの実行異常終了
= 4 CANCELED; //タスクがキャンセルされた
遮断= 5; //タスクが中断された
6 =中断; //タスクが中断され
コードをコピー

変数は、実行または例外の最終結果を示してあり、宣言しました:

プライベートオブジェクトの結果; 

変数は、タスクを表し実行中のスレッドがあります:

プライベート揮発性のスレッドランナー。

片方向リンクリストは、タスクの実行結果のためのスレッドが待機を表しあります:

プライベート揮発性WaitNodeのウェイター。

FutureTask Runnableオブジェクトを受け入れた場合、次のようにFutureTaskのコンストラクタ初期化呼び出し可能と状態、それはCallableオブジェクトにExecutors.callableを呼び出します。

公共FutureTask(Runnableを実行可能な、V結果){ 
    this.callable = Executors.callable(実行可能な、結果)。
    this.state = NEW; //呼び出し可能なの視認性を確保
}

タスクの実行サービスは、実行スレッド実行FutureTaskの方法、(実行)のコードを使用します。

コードをコピー
ます。public void実行(){ 
    場合(状態= NEW ||!
        UNSAFE.compareAndSwapObject(これ、runnerOffset、!
                                     ヌル、にThread.currentThread()))
        のリターン; 
    試す{ 
        呼び出し可能<V> C =呼び出し可能。
        IF(C = NULL &&状態== NEW!){ 
            Vの結果。
            ブールRAN; 
            {試みる
                )(= c.callをもたらします。
                RAN =はtrue。
            }キャッチ(ThrowableのEX){ 
                結果= NULL; 
                RAN = falseは、
                setException(EX); 
            } 
            IF(RAN)
                セット(結果)。 
        }
    }最後に、{ 
        状態がために落ち着くまで、//ランナーがnull以外でなければなりません
        //(実行するための同時通話を防ぐ)
        ランナー= NULL; 
        //状態は防ぐためにランナーをゼロにした後、再読み込みでなければなりません
        //漏れた割り込み
        int型S =状態。
        IF(S> =中断)
            handlePossibleCancellationInterrupt(S); 
    } 
}
コードをコピー

基本的なロジックは以下のとおりです。

  • 呼び出しメソッドは呼び出し可能呼び出し、すべての例外をキャッチ
  • 適切に実行された場合は、コール・セットの結果セットは、結果に保存します
  • 例外の実行が発生した場合、コールsetExceptionは例外が結果に保存され、例外を設定しますが、状態は同じではありません
  • 設定とセットsetException結果、状態を変更することに加えて、それはまたfinishCompletionを呼び出します、それは結果を待っている全てのスレッドをウェイクアップ

タスクの提出者のために、それは次のようにgetメソッドであるgetメソッド、コードの制限を通じて結果を取得します。

コードをコピー
公共のVのget(long timeout、TimeUnit unit)指定
    InterruptedExceptionある、ExecutionException、TimeoutExceptionをスロー{ 
    場合(単位== nullの)
        (新しいNullPointerExceptionがスロー)。
    int型S =状態。
    (S <= &&補完-----------------場合
        )(S = awaitDone(真、unit.toNanos(タイムアウト))<=完了すると)
        (新しいTimeoutExceptionを投げます)。
    リターン・レポート(複数可)。
}
コードをコピー

基本的なロジックは、タスクがまだ終了していない場合、我々は例外の状態に基づいて結果、レポートやリターンの結果を報告し、最終的な報告書の呼び出しは、コードをスローされ、待つことです。

コードをコピー
プライベートVレポート(INT s)はExecutionException {スロー
    オブジェクトX =結果を、
    もし(S == NORMAL)
        リターン(V)X。
    もし(S> = CANCELED)
        新しいCancellationExceptionを投げます(); 
    新しいExecutionException((のThrowable)X)を投げます。
}
コードをコピー

メソッドのコードをキャンセル:

コードをコピー
パブリックブールはキャンセル(ブールmayInterruptIfRunning){ 
    (状態= NEW!)であれば
        、リターンはfalse; 
    もし(mayInterruptIfRunning){ 
        場合(!UNSAFE.compareAndSwapInt(これ、stateOffset、NEW、中断))
            のリターンはfalse; 
        トン=ランナースレッド。
        (もし!T =ヌル)
            t.interrupt(); 
        UNSAFE.putOrderedInt(この、stateOffset中断)。//最終状態
    } 
    それ以外の場合(UNSAFE.compareAndSwapInt(これは、stateOffset、NEWは、CANCELED)!)は
        falseを返します。
    finishCompletion(); 
    trueを返します。
コードをコピー

基本的なロジック:

  • タスクが完了またはキャンセルされた場合は、偽を返します。
  • mayInterruptIfRunningがtrueの場合、割り込み割り込みスレッドを呼び出し、状態が中断に設定
  • falseの場合MayInterruptIfRunning、状態は解除設定されています
  • FinishCompletionは結果を待っているすべてのスレッドへの呼び出しを覚まします

invokeAll和invokeAny        

FutureTaskを理解し、我々はAbstractExecutorServiceの他の方法を見ていきます、invokeAll基本的なロジックは、各タスクのために、FutureTaskを作成し、呼び出しを実行するために実行し、その後、すべてのタスクの終了を待って、非常に簡単です。

invokeAnyが、それはこのクラスについては、ExecutorCompletionServiceを活用して、もう少し複雑達成し、invokeAnyを実現するために、我々は後の章で再導入します。

概要

このセクションでは、Javaおよび請負サービスの基本的な概念と原則で実行されるタスクを説明し、サービスの体現「懸念の分離」同時非同期開発のアイデアは、ユーザーがタスクのみを提出する必要がExecutorServiceの未来は、タスクや缶の結果を操作することにより、あなたは、スレッドの作成と内容の調整を心配する必要はありません。

このセクションでは、タスクごとに個別のスレッドを作成し、簡単な実装サービスSimpleExecutorServiceを達成するために、基本的な原則AbstractExecutorServiceとFutureTaskを説明しています。実際には、最も頻繁に使用されるサービスの実装がThreadPoolExecutorを達成するために、スレッドプールに基づいており、並行プログラミングのスレッドプールは、私たちは次のセクションを探索することができます非常に重要な概念と技術です。

おすすめ

転載: www.cnblogs.com/ivy-xu/p/12364862.html