AWTとSwingのペイント(paint)の仕組み Java API javadoc改善運動! Javaの手帖 Javaの手帖--GUI分冊 /ひとつ上をゆくJavaの教科書 「JavaServer Faces完全ガイド」補遺
javacのエラーメッセージ解説 「Javaの哲学」の恥かき 「Javaによるテキスト処理」の恥かき 「モア・サーブレット」の恥かき 恥かきのホームページ フォーラム「JavaBeginner」
UIDefaultsのkeyとデフォルト値「コア・サーブレット」の恥かき comp.lang.java.guiのFAQ日本語訳 「入門 正規表現」 「Javaネットワークプログラミングの真髄」 「Java並行処理プログラミング」

[NEW][正誤表][文献リスト][HOME]
[原書ホームページ][原書正誤表]

重要★原書の正誤表の随時改訂はこのサポートページ上に転記されませんので、
ときどき[原書正誤表]を見ることをおすすめします。

Java並行処理プログラミング--follow-up

  1. 080131
    ■インターネット協会Java研究部会Java読書会記録
     2006年7月から2007年3月まで、計9回の読書会で「Java並行処理プログラミング」の原書を取り上げています。会のログ(そのリンク集がここ)には、訳書のページ・ノンブルも併記されています。この読書会のメーリングリストに、私(岩谷)も最近参加しました。

  2. 080126
    ■原書の文献リストとURL集
     原書の文献リストが訳書に欠落していることに(遅まきながら!)気づきましたので、原書のものほぼそのままをHTML化しました。[ここ]でご覧になれます。


  3. 071105
    ■Future.cancel()と新forループとList.remove()
    私の最近のドジより:

     (1)Future.cancel()は、実行中のタスクをキャンセルしません。インタラプトするだけですから、タスクがそれに呼応してキャンセルするよう書かれていないかぎり、キャンセルはしません。これはFuture.cancel()のドキュメンテーションに書かれていることですが、つい、メソッドの名前に騙されてしまいます。名前の正しい解釈は、submit()したけどそのsubmit()をキャンセルする、でしょうね。

     (2)新しいforループ…for (element : list)…でListのremove()を呼び出すとConcurrentModificationExceptionが投げられます。このforループはイテレータを使うからです。そのことを、つねに意識して使う(つまりremove()するときはこの新forループを“使わない”)必要がありますね。この話題が初耳のかたは、本書5章のはじめのほうと、[JLSのこの節]を読んでみましょう。

  4. 070416
    ■クラスの初期化時に起きうるデッドロック
    --このプログラムは、デッドロックを起こし、ハングします。その理由を考えてみましょう。

    ------------------------------------------------------------------------------------
    
    //某社の入社試験問題
    //このプログラムは何をプリントしますか?
    public class Initializer{
      private static boolean initialized = false;
    
      static{
        Thread t = new Thread(new Runnable(){
          public void run(){
            initialized = true;
          }
        });
        t.start();
    
        try{
          t.join();
        }
        catch (InterruptedException e){
          throw new AssertionError(e);
        }
      }
    
      public static void main(String[] args){
        System.out.println(initialized);
      }
    }
    
    ------------------------------------------------------------------------------------


  5. 070410
    Intel Opinion Center: Parallel Programming
    --Slashdotのフォーラム上でIntelのCore開発者たちがプログラマたちの質問や意見に答える。

    Javaメモリモデルの致命的な欠陥, スレッドの問題点など、興味深い文献のリンクも紹介されています。


  6. 070212
    マルチコア/マルチプロセッサ時代のプログラミング
    (カリフォルニア大学バークレイ校のテクニカルレポート--061218)


    [部分引用] 古い常識と新しい常識

    1.電気は安く石は高い。
      石は安く多量の石を動かす電気は高い。
    2.消費電力の主要部分は駆動電源である。
      サーバやデスクトップ機では消費電力の40%以上が遺漏電力である(主に熱)。
    3.シングルプロセッサはエラーがピンのところでしか起きない。
      65nm以下になると、ソフト/ハード両面のエラーが急増する。
    4.既存の成果を利用することによって高度なハードウェアデザインが無限に可能。
      65nm以下で複雑高度なチップを作ろうとすると回路的/電気的障害が続出する。
    5.チップの研究開発において実機を試作できる。
      65nm/GHzのレベルでは、信頼性の高いプロトタイプを作ることは困難。
    6.実行性能の向上は遅延の低下と帯域幅の拡大の両方をもたらす。
      帯域幅の改善は少なくとも遅延の改善の二乗に比例する(遅延改善が重要)。
    7.乗算は遅く、load/storeは速い。
      load/storeは遅く(200clks)、乗算は速い(4clks)。
    8.命令レベルの並列化(ILP)の余地がいくらでもある。
      “ILPの壁”がある。
    9.プロセッサの速度は18か月で倍増する。
      電力消費量の壁+メモリの壁+ILPの壁で、2002年以降のカーブでは倍増に5年を要する。
    10.プロセッサはどんどん速くなるから並列化で苦労するのは無駄。
       速いプロセッサが得られるまで5年も10年も待つことになる。
    11.プロセッサのクロックレートを上げることが最大の性能向上手段だ。
       並列化(マルチコア化)がプロセッサの最大の性能向上手段だ。
    12.プロセッサ数とアプリケーションの実行性能は比例しない。
       並列コンピューティングに切り換えて得られる速度向上はめざましい。


  7. 061120
    JDK 6のjava.concurrent新機能紹介



  8. 061019
    ■EDT(AWT/Swingイベントディスパッチスレッド)の上で起動した一般タスクのスレッドのプライオリティを下げる件:

    [本書本文]
    10.3.2 応答性の劣化

     飢餓状態の横に並んでいるのが、応答性の劣化です。これはバックグラウンドのスレッドを使っているGUIアプリケーションでよく起きます。9章で、ユーザインタフェイスがフリーズしないように、長時間かかるタスクをバックグラウンドスレッドに移すためのフレームワークを開発しましたね。しかしCPU集約的なバックグラウンドタスクはイベントスレッドとCPUサイクルを争うので、やはり応答性に影響を与えます。CPU集約的なバックグラウンドの計算処理が応答性に悪影響を与えるときは、スレッドのプライオリティを変えてみてもいい場合のひとつです。そのタスクが本当にバックグラウンド的なタスク(完了が少々遅くなってもいい)なら、そのプライオリティを下げてフォアグラウンドのタスクの応答性を改善できます。

    [解説]
    EDTの上で作られるスレッドはプライオリティが高く設定されるので、これがアプリケーションの動作を阻害するときは、
    t.setPriority(Thread.NORM_PRIORITY);によりプライオリティを下げてください。

    [関連記事]
    Swing Urban Legend


  9. 061027
    ■Java 6における固有のロックの性能向上の件:

    [本書本文]
    13.2 実行性能への配慮

     Java 6は新しいアルゴリズムを使って固有のロックを管理します。それはReentrantLockが使っているアルゴリズムに似ているので、両者のスケーラビリティの差がかなり縮まりました。図13.1は固有のロックとReentrantLockの実行性能の違いをJava 5.0と正式リリース前のJava 6で比較した結果です(4-way Opteronシステム+Solarisオペレーティングシステム)。このグラフは、同じJVMの上でReentrantLockが固有のロックよりも“何倍速いか”という倍率を表しています。Java 5.0ではReentrantLockが断然優位ですが、Java 6では両者は接近しています。テストプログラムは11.5で使ったものと同じですが、今回は固有のロックとReentrantLockでガードされたHashMapのスループットを比較しています。
     ご覧のようにJava 5.0では、固有のロックの実行性能が1スレッド(争奪なし)と複数スレッドで極端に違います。ReentrantLockはスループットがそんなに低下せず、良好なスケーラビリティを示します。しかしJava 6ではがらりと変わって、固有のロックは争奪があっても崩れません。両者のスケーラビリティはほぼ肩を並べます。
    〔原注3:本書を書きはじめたころは、ReentrantLockはロックのスケーラビリティに関する究極の答えだと思われました。しかし1年足らずの間に、固有のロックが接戦を演じるようになりました。実行性能は単なる動く標的ではなく、高速で動く標的です。〕

    [解説]
    Java 6以降は、単純な同期化のために実行性能の向上をねらってReentrantLockなどを使う理由はなくなったと言えます。固有のロック(synchronizedキーワードの使用)のほうがシンプルで分かりやすければ、あえて新しいAPIを使う必要はありません。

    [関連記事]
    ReentrantLock vs binary Semaphore


  10. 061030
    ■CopyOnWriteArrayListのスレッドセーフ性について:

    [本書本文]
    5.2.3 CopyOnWriteArrayList
     CopyOnWriteArrayListはListの同期化クラスに置き換わるクラスで、一部のいちばんよくある状況でベターな並行性を提供し、イテレーションの間にコレクションをロックしたりコピーする必要性を解消します(同じくCopyOnWriteArraySetはSetの同期化クラスに置き換わります)。
     コピー・オン・ライト(copy-on-write,書き込みがあったらコピーを作る)コレクションは、実質的に不可変なオブジェクトが正しく公開されればそれにアクセスするときに同期化する必要がない、という事実からスレッドセーフ性を得ています。

    [解説]
     CopyOnWriteArrayList/ArraySetは、データのスナップショットをfinalな配列として公開します:
    
        public Iterator iterator() {
            return new COWIterator(getArray(), 0);
        }
        ---------
        private static class COWIterator implements ListIterator {
            /** Snapshot of the array **/
            private final Object[] snapshot;
            /** Index of element to be returned by subsequent call to next.  */
            private int cursor;
    
            private COWIterator(Object[] elements, int initialCursor) {
                cursor = initialCursor;
                snapshot = elements;
            }
    

     このような周到なイテレータもしかし、サブクラスが不用意にノーガードのクラスフィールドにしてしまうと、スレッドセーフ性は崩壊します(悪い例):
    
    public class DistributionSet extends CopyOnWriteArraySet{
      private Iterator i;
    
      public void enqueue(Object message){
        for (i = this.iterator(); i.hasNext(); ){
          EventListener listener = (EventListener)i.next();
          listener.eventObserved(message) ;
        }
      }
    
      public  boolean add(EventListener consumer){
        super.add(consumer);
        return true;  
      }
    } 
    
     そもそも、最近の設計常識or定石は「サブクラスづくりはやめましょう。組み立て(composition)と委譲(delegation)をもっぱら使いましょう」ということになっています。

    [関連記事]
    (参考)
    14.5.1 簡単なラッチ
     ・・・・・・・・
     ・・・・・・・・
     OneShotLatchは、現状のようにAQSに委譲せずに、AQSをextendsしても作れます。しかしそれは、いくつかの理由で良くありません[EJ Item 14]。AQSのサブクラスにすると、現状のOneShotLatchのシンプルなインタフェイス(メソッドがたった二つ)が損なわれ、またAQSのpublicメソッドはラッチのステートの破壊を許しませんが、呼び出し側がメソッドの使い方を間違えることは大いにありえます。java.util.concurrentのシンクロナイザはどれも、AQSを直接extendsしていません。それらはすべて、privateな内部クラスとしてAQSのサブクラスを作り、それに委譲しています。


--------------
■正誤表:

  [文献リスト]

51 7
【誤】スレッド拘束の使用を決定づけるのは
【正】スレッド拘束を使おうという決定は

51 8
【誤】決定をした結果
【正】決定の結果

205 1-2
【誤】並列化の良い候補
【正】並列化するタスクの良い候補

236 4-6
【誤】transferMoneyのような二つのAccountのどちらかのロックを入手する前に引き分けロックを入手すると、二つのロックを恣意的な順序で入手する危険なタスクを、確実に
【正】二つのAccountのどちらかのロックを入手する前に引き分けロックを入手するようにすると、二つのロックを恣意的な順序で入手するtransferMoneyのような危険なタスクを、確実に

399 7
【誤】GUIアプリケーションゲーム
【正】GUIアプリケーション、ゲーム


(さくいん追加)

Linux … 5

ノンブロッキングI/O … 5

無から有が生まれない安全性 … 42

cancelをカスタマイズ(オーバライド)する … 169

Futureのcancelをカスタマイズ(オーバライド)する … 169

out-of-thin-air safety … 42

not-out-of-thin-air safety … 42

RunnableFuture … 169

newCachedThreadPoolとnewFixedThreadPool … 197

newFixedThreadPoolとnewCachedThreadPool … 197

-------------------------------------------


AWTとSwingのペイント(paint)の仕組み Java API javadoc改善運動! Javaの手帖 Javaの手帖--GUI分冊 /ひとつ上をゆくJavaの教科書 「JavaServer Faces完全ガイド」補遺
javacのエラーメッセージ解説 「Javaの哲学」の恥かき 「Javaによるテキスト処理」の恥かき 「モア・サーブレット」の恥かき 恥かきのホームページ フォーラム「JavaBeginner」
UIDefaultsのkeyとデフォルト値「コア・サーブレット」の恥かき comp.lang.java.guiのFAQ日本語訳 「入門 正規表現」 「Javaネットワークプログラミングの真髄」 「Java並行処理プログラミング」