Javaでの非同期処理の基本
非同期処理が概観できたところで、Javaによる非同期処理の記述方法を紹介していきましょう。
Javaの非同期処理のソースコードパターン
具体的な解説に入る前に、Javaで単純な非同期処理を実行させるためのソースコードパターンを提示しておきましょう。それは、リスト1のコードとなります。
@Override protected void onCreate(Bundle savedInstanceState) { 〜省略〜 BackgroundTask backgroundTask = new BackgroundTask(); // (1) ExecutorService executorService = Executors.newSingleThreadExecutor(); // (2) executorService.submit(backgroundTask); // (3) } private class BackgroundTask implements Runnable { // (4) @Override public void run() { // (5) Log.i("Async-BackgroundTask", "ここに非同期処理を記述する"); // (6) } }
Javaの非同期処理の中心であるExecutor
Javaには、マルチスレッドを効率よく扱うパッケージとしてjava.util.concurrentが用意されています。前節での解説の通り、Javaでの非同期処理は、すなわちマルチスレッド処理です。そのため、このjava.util.concurrentパッケージのクラス群を利用することになります。その中心となるのがExecutorです。
ただし、Executorはインターフェースですので、実際にはExecutorを実装したクラスを利用します。実装クラスは自作することも可能ですが、通常はもちろんjava.util.concurrentに用意されたクラスの中から用途に合わせたものを利用します。
さらに、利用する際も、該当クラスをnewするのではなく、Executorを実装したインスタンスを生成するファクトリクラスであるExecutorsのメソッドを利用して作成されたインスタンスを利用します。Executorsのインスタンス生成メソッドとして、主なものを以下に列挙します。
-
newSingleThreadExecutor()
単純に別スレッドで動作するインスタンスを生成 -
newFixedThreadPool()
指定のスレッド数を確保した上で処理を実行できるインスタンスを生成 -
newCachedThreadPool()
スレッドをキャッシュした上で再利用できるインスタンスを生成 -
newScheduledThreadPool()
別スレッドで指定時間おきに処理を実行できるインスタンスを生成
リスト1では、単純に別スレッドにするだけで問題なく動作しますので、(2)のように、newSingleThreadExecutor()を利用しています。
Executorsによって生成されるExecutorService
リスト1の(2)を見てもわかるように、ExecutorsのnewSingleThreadExecutor()メソッドの戻り値は、ExecutorServiceインスタンスです。ExecutorServiceというのは、Executorインターフェースの子インターフェースであり、このExecutorServiceインスタンスが、Javaで非同期処理を行うには便利です。
具体的には、ExecutorServiceのsubmit()メソッドを実行することで、別スレッドで処理を実行、つまり、非同期処理が行われます。それが、リスト1の(3)です。
submit()の引数はRunnableインスタンス
そのExecutorServiceのsubmit()メソッドは、引数としてRunnableインスタンスを必要とします。ただし、Runnableはインターフェースですので、その実装クラスを用意する必要があります。それが、リスト1の(4)であり、その実装クラスであるBackgroundTaskをnewしているのがリスト1の(1)です。
なお、リスト1では、Runnableの実装クラスとしてのBackgroundTaskクラスをprivateなメンバクラスとして用意していますが、もちろん、クラス名はこの限りではありませんし、クラス自体を無名クラスやラムダ式として記述してもかまいません。本項では可読性を確保するためにメンバクラスとして記述することにします。
非同期処理の実態はrun()メソッド内の処理
Runnableの実装クラスは、run()メソッドを実装(オーバーライド)する必要があります。それが、リスト1の(5)です。そして、ExecutorServiceのsubmit()によって実際に非同期で処理されるのは、引数として渡されるRunnable実装クラスのこのrun()メソッドに記述された処理なのです。リスト1では、(6)が該当し、ここでは単純にログへの書き出し処理のみを行っています。