私のAndroidプロジェクトでは、コードを非同期で実行する必要がある場所がたくさんありました(Webリクエスト、dbの呼び出しなど)。これは長時間実行されるタスクではありません(最大数秒)。 )これまで、私は新しいスレッドを作成し、タスクで新しい実行可能ファイルを渡すことでこの種のことを行っていました。しかし最近、スレッドと並行性に関する記事をJavaで読み、理解しました。すべてのタスクに対して新しいスレッドを作成することは良い決断ではありません。
これで、5つのスレッドを保持するThreadPoolExecutor
クラスにApplication
を作成しました。コードは次のとおりです。
public class App extends Application {
private ThreadPoolExecutor mPool;
@Override
public void onCreate() {
super.onCreate();
mPool = (ThreadPoolExecutor)Executors.newFixedThreadPool(5);
}
}
また、Runnableタスクをエグゼキュータに送信するメソッドがあります。
public void submitRunnableTask(Runnable task){
if(!mPool.isShutdown() && mPool.getActiveCount() != mPool.getMaximumPoolSize()){
mPool.submit(task);
} else {
new Thread(task).start();
}
}
したがって、コードで非同期タスクを実行する場合は、App
のインスタンスを取得し、submitRunnableTask
メソッドを呼び出してランナブルを渡します。ご覧のとおり、スレッドプールにタスクを実行するための空きスレッドがあるかどうかも確認します。ない場合は、新しいスレッドを作成します(これが発生するとは思いませんが、いずれにせよ...私はしません。タスクをキューで待機させてアプリの速度を低下させたい)。
アプリケーションのonTerminate
コールバックメソッドで、プールをシャットダウンしました。
だから私の質問は次のとおりです:この種のパターンは、コードで新しいスレッドを作成するよりも優れていますか?私の新しいアプローチにはどのような長所と短所がありますか?まだ気付いていない問題が発生する可能性はありますか?非同期タスクを管理するために、これよりも優れたアドバイスをいただけますか?
P.S. AndroidとJavaの経験はありますが、並行性の第一人者にはほど遠いです)ですから、この種の質問ではよく理解できない側面があるかもしれません。アドバイスをいただければ幸いです。
この回答はあなたのタスクが短いことを前提としています
この種のパターンは、コードで新しいスレッドを作成するよりも優れていますか?
それは良いですが、それでも理想からは程遠いです。あなたはまだ短いタスクのスレッドを作成しています。代わりに、たとえばExecutors.newScheduledThreadPool(int corePoolSize)
によって別のタイプのスレッドプールを作成する必要があります。
行動の違いは何ですか?
FixedThreadPool
には常に使用するスレッドのセットがあり、すべてのスレッドがビジーの場合、新しいタスクがキューに入れられます。ScheduledThreadPool
クラスによって作成された(デフォルトの)Executors
には、minimumスレッドプールがあり、アイドル状態でも。新しいタスクが入ったときにすべてのスレッドがビジーの場合、そのスレッドの新しいスレッドを作成し、完了してから60秒後にスレッドを破棄します。再び必要にならない限り。2つ目は、自分で新しいスレッドを作成しないようにすることができます。この動作は「スケジュール済み」の部分がなくても実現できますが、エグゼキュータを自分で構築する必要があります。コンストラクターは
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
さまざまなオプションを使用して、動作を微調整できます。
一部のタスクが長い場合...
そして、私は長いことを意味します。ほとんどのアプリケーションライフタイムと同様(リアルタイム双方向接続?サーバーポート?マルチキャストリスナー?)。その場合、Runnable
をエグゼキュータに入れることは有害です-標準のエグゼキュータはそれに対処するように設計されていません、そしてそれらのパフォーマンスは劣化します。
固定スレッドプールについて考えてみてください。実行時間の長いタスクが5つある場合、新しいタスクは新しいスレッドを生成し、プールの可能なゲインを完全に破壊します。より柔軟なエグゼキュータを使用する場合-一部のスレッドは共有されますが、常にではありません。
経験則は
質問に答えるには—はい、次の理由により、Executorを使用する方が新しいスレッドを作成するよりも優れています。
Ordousのコメントに基づいて、1つのプールのみで機能するようにコードを変更しました。
public class App extends Application {
private ThreadPoolExecutor mPool;
@Override
public void onCreate() {
super.onCreate();
mPool = new ThreadPoolExecutor(5, Integer.MAX_VALUE, 1, TimeUnit.MINUTES, new SynchronousQueue<Runnable>());
}
}
public void submitRunnableTask(Runnable task){
if(!mPool.isShutdown() && mPool.getActiveCount() != mPool.getMaximumPoolSize()){
mPool.submit(task);
} else {
new Thread(task).start(); // Actually this should never happen, just in case...
}
}
ですから、これが他の誰かに役立つことを願っています。経験豊富な人々が私のアプローチについてコメントをいただければ、彼らのコメントに感謝します。