クライアントとServletの関係
Servletを簡単に言ってしまえば、(1)クライアントからリクエストを受け付け、(2)リクエストに応じた処理を行い、(3)クライアントへレスポンスを返すクラスです(非常に簡単そうに聞こえますが、実は基礎を知らないと落とし穴に落ちやすいクラスでもあります)。図2がそれを概念化したものです。
Servletはクライアントの受付係です。このように言われても理解できないと思うので、一旦Servletから離れ、次のような状況を考えてみます。
例えば、Aさんからリクエストを受け付けたらインスタンスAを生成し、Bさんからリクエストを受け付けたらインスタンスBを生成し・・・とクライアントごとにインスタンスを生成するという教科書どおりのオブジェクト指向を利用した方法をとった場合、ちょっとしたWebサイトでもあっという間にメモリリーク(メモリを使い果たし何も処理ができなくなること)を起こしてしまいます。資源が無限にあるとすれば、この方法が1番いい方法と言えるでしょう。ただし、資源が有限だとすれば、1クライアントに1インスタンスを与えていてはメモリがすぐに不足します。
上記の問題に対処するには、アクセス数の制限をかけるという方法もあります。ただし、制限をかけすぎるとビジネスチャンスを失うことにもなりかねません。また、GoogleやAmazonなどのクラウドを利用することでアクセス数に応じてリソースを柔軟に増減させることで対処することも可能です。どの方法を取るにしろ、メモリを節約するプログラミングをすることが肝要です。
それでは、メモリを節約する方法を考えましょう。ServletXはクライアントAからリクエストを受け付けたら(図2の1)スレッドAを生成します(図2の2)。スレッドとインスタンスの区別は大切です。インスタンスはそれ自身のメモリ空間が存在します。それに対し、スレッドはそれ自身のメモリ空間を持ちません(スレッドは大きなテーマのため、最後に参考文献を挙げています)。そのスレッドAがクライアントAの要求に応えます。再度クライアントAからServletXに対し要求を出しても(図2の4)実際にはスレッドAがその要求に応えます。これ以降ServletXとクライアントAは直接やりとりしません。クライアントBからリクエストを受け付ければスレッドBを生成する、クライアントCからリクエストを受け付ければスレッドCを生成するというようにしてメモリを節約します。厳密にいえば、リクエストの度にスレッドを生成するのはコストがかかるため、スレッドプールというものをアプリケーションサーバは持っています。リクエストがあればプールから取り出します。
スレッドを生成させるコードを書く必要はありません。ServletXを普通に呼び出せばスレッドは生成されます。ただし、初めてServletXクラスを呼び出す場合のみインスタンスが生成されます。これは後ほどServletのライフサイクルとして紹介します。実際にスレッドは入門者には難しい概念です。ただし、Webアプリケーションに限れば、以下のことに注意すれば問題を回避できます。
- 1つのServletから生成されたスレッドはメモリ空間を共有する
このたった1つの事実を知ることで、多くのバグを予防できるのです。つまり、クライアントAとクライアントBは同じメモリ空間を使用します。これは非常に困ったことになります。
サンプルプログラムとしてひたすら数字を足すプログラムを考えましょう。クライアントAがXというメモリ領域に100という数値を書き込んでも、その後にクライアントBがXに200という数値を書き込むと最初の100という数値に200が加算され合計が300となってしまいます。この時点でクライアントBはなぜ200ではなく300なのか首を傾げることになります。クライアントAはそれを知らずにXに300を足して合計400にしようとしても、結果は600となってしまいます。つまり、クライアント毎の情報は保持されないということを意味しています。これは由々しきことです。クライアント毎の情報を保持できないのであれば、Webアプリケーションの構築ができなくなってしまうからです。
それでは、次ページから実際にプログラムを作成し、実験してみましょう。

