Javaプラットフォームは、まるで神から人間に火を盗み与えてくれたプロメテウスのように、並列性の神からマルチスレッドの「火」を盗み、我ら開発者にも手の届く存在にしてくれたかのようです。ただ、人間が火を使えるようになるということは、火傷の危険があるということでもあります。このモデルでは、通常、スケジュールされたタスクの完了を通知するためにリスナーを使うよう求められます。これによってプログラムの通常の処理の流れが分断されました。普通、これは事態の追跡を困難にします。
さらに、何ができるか知った開発者は、現状に満足せずに一層高いパフォーマンスを求めるようになりました。タスクの待ち行列処理を簡素化するよりも、優先度別の待ち行列を持つことを望んだのです。Doug Leaは、これらの課題に取り組むために、Syncs、Barriers、Rendezvousesなどの興味深い並列構造を導入したことで知られています。これは非常に画期的なことで、これにより上級開発者は強力なソフトウェアを開発しました。しかし、一般の開発者は、まだ問題に突き当たることがよくありました。
状態に基づいて動作するオブジェクト指向プログラミングでは、複数のタスクが状態の更新または読み取りを行おうとすると、処理が分かりにくくなります。マルチスレッド処理に関係するエラーは、ランダムに発生することが多く、しかも再現性が低いので、特にデバッグが困難なエラーの1つだと言えます。Swingチームはこの点をよく承知していたため、Swingを単一スレッドのAPIにすることを決断しました。この制限に不平を唱える人もいましたが、この決定によって開発者による新しいコンポーネントの作成が簡素化されたことは間違いありません。Javaが発表されて10年以上経た現在も「Java Concurrency in Practice」がよく読まれていることから分かるように、多くのプログラマはまだこの問題と格闘しています。
本番のコードで次のようなものを見かけたら、どこかに問題があります。
synchronized(new Object() {}) {
// ... unsynchronized synchronized block here
// DO NOT EMULATE THIS CODE! Why won't this do what
// intended?
}
ひとまず低レベルの状態管理から離れて、ハイレベルの機能、例えば、Webサービス呼び出しの発行、データベースの問い合わせ、XMLの変換などについて考えてみましょう。これらのワークフローではプロセス間の関係やサービスを起動する頻度などに関して、新たな課題が生じます。それらのプロセスはビジネスルールに基づいて変化することが多いので、言語レベルの構造に基づいて、こういった活動の連携を図るのは好ましくありません。ThreadPoolsやExecutorといったフレームワークや構造を十分活用するためには、Runnableのようなインターフェースを中心にコードを作成し、コードがどのようなコンテキストの下で使われるか把握することが必要です。あるステップで処理をブロックしたいこともあれば、そうでないこともあります。複数のデータソースに対する非同期呼び出しを連携させ、その結果をHTMLに変換したいこともあるでしょう。あらゆるものを独立した無関係な実行可能コードのブロックに分割できなければ、複数のCPUの能力を余すことなく利用する段階には到達できないでしょう。また、そのように動作するアプリケーションを何とか設計したとしても、スレッドで何ができて何ができないかはコードの展開先の環境(最新のアプリケーションサーバーなど)次第で決まります。
Javaにはプラットフォームおよび言語レベルで多くの強力な機能がありますが、それらを正しく効果的に使うことは、多くのアプリケーションにおいて必要以上に困難になっています。こうした課題に背を押されて、開発者はErlang、Scala、Clojureといった代替言語の使用を検討しつつあります。これらの関数型言語やハイブリッド言語では、ソフトウェアトランザクションメモリというような新しい概念や、並行ロックで保護しなければならない状態の数を減らすプログラミングスタイルが導入されています。ScalaとClojureはどちらもJVMで動作するため、相当量のJavaコードを再利用することが可能です。しかし、Erlangでは新しいツールやランタイム環境が必要です。
次世代のランタイム環境に関心があるなら、単純に言語を乗り換えるよりも、既に作成してあるソフトウェアの大半を再利用しつつ、システムのコアやCPUをフルに利用するソフトウェアを書くことを検討した方が簡単かもしれません。ここでのポイントは、オブジェクト指向の発想を情報リソース中心の発想に軌道修正することです。実装技術としてのオブジェクトはまだ使いますが、変更に柔軟に対応し、高い並行性を保つようにコードの依存関係を設計します。
