Item 84: Don’t depend on the thread scheduler

# 84.スレッドスケジューラに依存してはならない

* スレッドスケジューラに正確性、パフォーマンスを依存したプログラムには移植性がない。スレッドをどのように実行するかはOS依存するからである。
* 信頼のおけるプログラムにするためには、処理実行平均スレッド数がプロセッサの数を大きく上回らないようにすべきである。
* runnableなスレッドの数を低く抑えるためには、効果のある処理をしないときには、スレッドを走らせないようにすべきである。ExecutorFrameworkの観点からは、スレッドプールのサイズを適切にし、タスクを短く抑えるようにすべき（短すぎると割り当てのためのオーバーヘッドが大きいのでダメ）。
* スレッドはbusy-wait状態、つまり、共有オブジェクトの状態が変化したか否かを繰り返しチェックしに行く状態、を避けるべきである。busy-waitはプロセッサに大きな負荷を与え、他のスレッドが効果のある処理を行うことを妨げてしまう。
以下の実装（SlowCountDownLatch）のようなCountDownLatchを使うとbusy-wait状態となり、普通のCountDownLatchを使うよりも遅くなる。

```java
package tryAny.effectiveJava;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class CountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        Executor e = Executors.newFixedThreadPool(1000); // ここの引数をtimeの第2引数の値未満にすると処理が終わらない。

        System.out.println("time :" + time(e, 1000, () -> System.out.print("")));
        System.out.println("time2:" + time2(e, 1000, () -> System.out.print("")));
    }

    // Simple framework for timing concurrent execution
    public static long time(Executor executor, int concurrency, Runnable action) throws InterruptedException {
        CountDownLatch ready = new CountDownLatch(concurrency);
        CountDownLatch start = new CountDownLatch(1);
        CountDownLatch done = new CountDownLatch(concurrency);
        for (int i = 0; i < concurrency; i++) {
            executor.execute(() -> {
                ready.countDown(); // Tell timer we're ready
                try {
                    start.await(); // Wait till peers are ready
                    action.run();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    done.countDown(); // Tell timer we're done
                }
            });
        }
        ready.await(); // Wait for all workers to be ready
        long startNanos = System.nanoTime();
        start.countDown(); // And they're off!
        done.await(); // Wait for all workers to finish
        return System.nanoTime() - startNanos;

    }

    public static long time2(Executor executor, int concurrency, Runnable action) throws InterruptedException {
        SlowCountDownLatch ready = new SlowCountDownLatch(concurrency);
        SlowCountDownLatch start = new SlowCountDownLatch(1);
        SlowCountDownLatch done = new SlowCountDownLatch(concurrency);
        for (int i = 0; i < concurrency; i++) {
            executor.execute(() -> {
                ready.countDown(); // Tell timer we're ready
                try {
                    start.await(); // Wait till peers are ready
                    action.run();
                } finally {
                    done.countDown(); // Tell timer we're done
                }
            });
        }
        ready.await(); // Wait for all workers to be ready
        long startNanos = System.nanoTime();
        start.countDown(); // And they're off!
        done.await(); // Wait for all workers to finish
        return System.nanoTime() - startNanos;

    }

    static class SlowCountDownLatch {
        private int count;

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

        // ここの実装がめっちゃbusy-wait
        public void await() {
            while (true) {
                synchronized (this) {
                    if (count == 0)
                        return;
                }
            }
        }

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

}
```

* 対象のスレッドが十分なCPU時間を確保できないからといって、Thread.yieldを用いてはならない。一時的に事態が好転するかもしれないが、移植性のあるものではない。そうするよりも、アプリケーションの構成を見直し、並列実行されるスレッド数を減らすようにするべきである。
* Threadのpriorityも同様に移植性がなくあてにはならない。