The King's Museum

ソフトウェアエンジニアのブログ。

【Effective Java】項目72:スレッドスケジューラに依存しない

プログラムの正しさやパフォーマンスをスレッドスケジューラーの動作に依存させてはいけません。

実行可能なスレッド数はプロセッサの数と比べて非常に大きくならないようにするべきです。 そうしておけば、スレッドスケジューラーがどのようなアルゴリズムを採用していてもプログラムは正しく動作します。

実行可能なスレッド数を少なくするためにはスレッド数を適切に制限するべきです。 エグゼキューターフレームワーク項目68)では、スレッド数を制限したスレッドプールを作成できます。

ビジーウェイト

特定の条件を満たすまでスレッドを待機させるためにビジーウェイトを使うべきではありません。 ビジーウェイトはプログラムを脆弱にし、プロセッサへの不可が増大します。

例えば次のようなビジーウェイトを用いた CountDownLatch はとても遅いです。

public class SlowCountdownLatch {
    private int count;

    public SlowCountdownLatch(int count) {
        if (count < 0) {
            throw new IllegalArgumentException(count + " < 0");
        }
        this.count = count;
    }

    public void await() {
        while (true) {
            synchronized (this) {
                if (count == 0) {
                    return;
                }
            }
        }
    }

    public synchronized void countDown() {
        if (count != 0) {
            count--;
        }
    }

}

Thread.yield()

Thread.yield() は他のスレッドに処理を実行させる機会を与えます。 しかし、機会を与えるだけで実際に他のスレッドの処理が実行されるとは限りません。

ある JVM 実装でパフォーマンスが改善する yield() 呼び出しが、他の実装では動作しないかもしれません。 実際、ある JVM 実装において yield() は何もしません。

Thread.yield() を利用する唯一の箇所はテストでした。 テストで yield を使うと実行パターンの状態空間の数が増え、バグをあぶり出すことができました。

しかし、前述したように JVM 実装によっては挙動が異なるため、普通は Thread.sleep(1) を使うべきです。 Thread.sleep(0) は実装によっては何もしないので、Thread.sleep(1) を使うべきです。

(c) The King's Museum