2012/03/22

Android:CountDownLatchで同期をとる


Androidに限った話ではありませんが、java.util.concurrentパッケージは同期・非同期
処理を実装する上で便利なクラスが数多くあります。

今回はそんなconcurrentパッケージから、他スレッドでの操作完了を待機する同期支援
クラスのCountDownLatchを使ってみます。
http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/concurrent/CountDownLatch.html

CountDownLatchクラスの主なメソッドはawaitとcountDownです。
CountDownLatchは単純なON/OFFを持ったラッチとして機能します。

awaitを呼び出したスレッドは、countDownを呼び出すスレッドによりラッチが開放される
まで待機します。
ラッチの開放にかかるアクション回数はコンストラクタで指定します。
アクションをN回で初期化された場合、countDownがN回実行されるまでawaitを呼び出した
スレッドは待機します。

例えば、2つのサービスをbindServiceで接続する必要があり、
とある処理αを実行する準備として、2つのサービスに接続済みである必要がある場合。
CountDownLatchを使用してサービスへの接続を待機する簡単なコードは下記になります。
// バインドするサービスクラス群
private final Class<?>[] SERVICE_CLASSES = new Class<?>[] {
        MyServiceA.class,
        MyServiceB.class
};

// バインドするサービス数でN初期化
private final CountDownLatch mLatch = new CountDownLatch(SERVICE_CLASSES.length);

private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.e("yuki", "onServiceConnected");

        // サービスが接続されたらカウントダウン
        mLatch.countDown();
    }

    //...
};

private void hoge() {
    // サービスの接続待ちをするスレッド
    new AsyncTask<Void, Void, Void>() {
        @Override
        protected Void doInBackground(Void... params) {
            try {
                // 全サービスの接続待ち
                mLatch.await();
            } catch(InterruptedException e) {
            }
            return null;
        }

        protected void onPostExecute(Void result) {
            Log.e("yuki", "全サービスの接続完了");
        };
    }.execute();

    // サービスをバインド
    for (Class<?> serviceClass : SERVICE_CLASSES) {
        Intent intent = new Intent(this, serviceClass);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }
}

# 2012/06/15追記

IBM DeveloperWorksにあるCountDownLatchサンプルコード
「CountDownLatch: レースに行きましょう」が素敵です。
http://www.ibm.com/developerworks/jp/java/library/j-5things5.html#N100B3

以上です。