Java EE 勉強会 (第40回)

Java 並行処理プログラミング

第12章「並行プログラムを試験する」。担当は杉田さん。
並行プログラムの自動テストの方法はなかなか興味深かった。
帰宅後の検証結果もあわせてメモ。

P.282 の List 12-3 はブロックしなくてもエラーにならないのでは? by 和泉さん

void testTakeBlocksWhenEmpty() {
    final SemaphoreBoundedBuffer<Integer> bb
            = new SemaphoreBoundedBuffer<Integer>(10);
    Thread taker = new Thread() {
        public void run() {
            try {
                int unused = bb.take();
                fail(); // ここに来たら、エラー
            } catch (InterruptedException success) {}
        }};
    try {
        taker.start();
        Thread.sleep(LOCKUP_DETECT_TIMEOUT);
        taker.interrupt();
        taker.join(LOCKUP_DETECT_TIMEOUT);
        assertFalse(taker.isAlive());
    } catch (Exception unexpected) {
        fail();
    }
}

確かに、fail() は呼ばれるけれど、taker のスレッドが落ちるだけで、JUnit 上ではエラーにならない。あと、テストメソッドは public にしないと。例えば、こんなふうに書く必要がある。

public void testTakeBlocksWhenEmpty() {
    final SemaphoreBoundedBuffer<Integer> bb
            = new SemaphoreBoundedBuffer<Integer>(10);
    final boolean[] failed = new boolean[1];
    Thread taker = new Thread() {
        public void run() {
            try {
                int unused = bb.take();
                failed[0] = true; // ここに来たら、エラー
            } catch (InterruptedException success) {}
        }};
    try {
        taker.start();
        Thread.sleep(LOCKUP_DETECT_TIMEOUT);
        taker.interrupt();
        taker.join(LOCKUP_DETECT_TIMEOUT);
        assertFalse(taker.isAlive());
        // ここで検証
        assertFalse(failed[0]);
    } catch (Exception unexpected) {
        fail();
    }
}

P.288 の List 12-7 で「ヒープをスナップショットする」の詳細は?

class Big { double[] data = new double[100000]; }

void testLeak() throws InterruptedException {
    BoundedBuffer<Big> bb = new BoundedBuffer<Big>(CAPACITY);
    int heapSize1 = /* ヒープをスナップショットする */;
    for (int i = 0; i < CAPACITY; i++)
        bb.put(new Big());
    for (int i = 0; i < CAPACITY; i++)
        bb.take();
    int heapSize2 = /* ヒープをスナップショットする */;
    assertTrue(Math.abs(heapSize1 - heapSize2) < THRESHOLD);
}

標準 API だけでやろうとすると、例えばこんな感じ?

private int snapshotHeap() {
    System.gc();
    Runtime r = Runtime.getRuntime();
    long total = r.totalMemory();
    long free = r.freeMemory();
    long heap = total - free;
    return (int) heap;
}

勉強会でも話題になっていたけれど、System.gc() は比較的ちゃんと GC してくれる。
ただし、元の testLeak() だと、heapSize1 より heapSize2 の方が小さくなってしまうことが多いみたい。heapSize1 の前にも、メモリを大量に使用する処理を入れると良さそう。

public void testLeak() throws InterruptedException {
    BoundedBuffer<Big> bb = new BoundedBuffer<Big>(CAPACITY);
    // ここから、
    for (int i = 0; i < CAPACITY; i++)
        bb.put(new Big());
    for (int i = 0; i < CAPACITY; i++)
        bb.take();
    // ここまで追加
    int heapSize1 = snapshotHeap();
    for (int i = 0; i < CAPACITY; i++)
        bb.put(new Big());
    for (int i = 0; i < CAPACITY; i++)
        bb.take();
    int heapSize2 = snapshotHeap();
    assertTrue(Math.abs(heapSize1 - heapSize2) < THRESHOLD);
}

P.289 の List 12-9 で、

ExecutorService exec = Executors.newFixedThreadPool(MAX_SIZE);

は、引数に threadFactory を渡してあげないとダメ。

ExecutorService exec = Executors.newFixedThreadPool(MAX_SIZE, threadFactory);

12-2-3「応答性を計測する」で、公平モードにすると、不公平モードの最大値よりも時間がかかってしまう。何でも公平にするってことはコストがかかるんだなぁと思った。

LDAP でとらぶった時

by 和泉さん
パラメータの設定の仕方とか、NamingEnumeration はちゃんと close しようとか。

WebWork2 ネタ

by もなじろうさん
SAStruts に (が?) そっくりだなぁという印象。Struts 2 使いますと言うより、Struts 1.x ベースの SAStruts 使いますと言った方が通りやすいってことか?
アーキテクチャの話もあったみたいだけれど、用事があったので途中で退席。