■サーブレット編5■サーブレット固有の問題
ここまで順に読んできて、課題等を試してきて、ここでいきなり「どんでん返し」です。
・サンプルの実行方法サンプルで実験をするにはまずブラウザを起動して、ctst_svltを開いておいてください。そうすると次のような画面が表示されます。
この中の「ID」、「文字」、「色」に適当な値を指定しておいてください。「ID」と「文字」は1文字入れておけば十分です。
・「文字」と「色」(文字の色です)をサーブレットのインスタンス変数に格納する
サーブレット側で以上の処理を行った後、サンプルのクラスを呼び出します。サンプルクラスを new 演算子でインスタンス化する際、引数でこの「ID」、「文字」、「色」などの情報を渡しています。クラス側ではコンストラクタの中でこれらの情報を、クラスのインスタンス変数に格納しています。
・インスタンス化されるとき、「ID」、「文字」、「色」をクラスのインスタンス変数に格納する
というように、やっている内容はサーブレット側と同じようなことです。ただそれを行っている母体がサーブレットか通常のクラスかの違いです。
・次の段階の実験(危険その1)
ブラウザを一つだけ起動した状態で動作内容の確認が終わったら、それをいったん閉じて(終了させて)、今度はブラウザを二つ起動してください。そして両方のブラウザでctst_svltを開いておきます。
・さらに次の段階の実験(危険その2)
ブラウザを二つ起動させた実験が終わったら今度は、ブラウザを閉じずに、そのまま両方の実行ボタンを押してみましょう(二つ目のブラウザの実行ボタンを押すタイミングは、先ほどの実験と同じ方がいいでしょう)。
・もっとやばい実験(危険その1から派生)今度はctst_svlt.javaのソースを一部修正します。クラスの変数宣言、doGet メソッドの変数宣言はこうなっています。 public class ctst_svlt extends HttpServlet { // private PrintWriter w_out; private String wc_dat; // 出力文字列 private int wc_cnt; // 変更カウント private String wc_chclr; // 表示文字の色 private String wc_chr; // 表示文字 public void doGet (HttpServletRequest p_request, HttpServletResponse p_response) throws IOException, ServletException { PrintWriter w_out = null; ctst_cls w_ctst_cls = null; SimpleDateFormat w_dfmt = null; GregorianCalendar w_gclndr = null; String w_id = null; // ブラウザID int w_lpcnt = 0; // ループカウンタ
PrintWriter 型の変数w_outはインスタンス変数とローカルの変数の二つ定義していますが、インスタンス変数は注釈になっています。
public class ctst_svlt extends HttpServlet { private PrintWriter w_out; // こちらは注釈を外す private String wc_dat; // 出力文字列 private int wc_cnt; // 変更カウント private String wc_chclr; // 表示文字の色 private String wc_chr; // 表示文字 public void doGet (HttpServletRequest p_request, HttpServletResponse p_response) throws IOException, ServletException { // PrintWriter w_out = null; // こちらを注釈にする ctst_cls w_ctst_cls = null; SimpleDateFormat w_dfmt = null; GregorianCalendar w_gclndr = null; String w_id = null; // ブラウザID int w_lpcnt = 0; // ループカウンタ
この修正後にコンパイルし、先ほどと同じように二つのブラウザを起動して実行してみてください。
◇実行結果とそうなる理由(危険その2)の実験の実行結果は概ね次のようになると思います(ブラウザ1側で一つ目のデータ表示がされた段階でブラウザ2の実行ボタンを押した場合)。
まず変更する文字列データ、文字列に結合する文字データ、カウンタ、は、サーブレットでインスタンス変数として定義しているところが原因です。 ブラウザ1側で最初の ctst_svlt:7:ID=A < を表示した後、ブラウザ2側のサーブレットが開始され、インスタンス変数の変更する文字列データをクリアし、文字列に結合する文字データに画面で指定した文字と色を設定しています。 次の ctst_svlt:9:ID=A >> ですが、ブラウザ2側で変更した結果に対して文字列を連結した形になっています。ブラウザ1側で結合する文字は"<"のはずなのですが、これもブラウザ2側で設定した文字と色になっています。 またカウンタ情報もブラウザ1側では7、8、9となるはずが7、9、11と飛んでおり、ブラウザ2側と併せて見ると7、8、9、10、11、12と1ずつ足されています。 しかしその後のctst_cls.javaで表示している内容はctst_svlt.javaと同様に、変更する文字列データ、文字列に結合する文字データ、カウンタ、はクラスのインスタンス変数として定義してあっても混在することはありませんでした。それぞれの画面で指定した文字が結合され、カウンタ情報もちゃんと画面毎に1ずつ足されています。 ・これの原因
結論を先に書くとサーブレットのインスタンス変数は、いわゆる大域変数(グローバル変数)になってしまうためです。
※ここはOS(オペレーティングシステム)周りの細かい話なんで、興味ない人は飛ばしてもらって構いません。 スレッドとは コンピュータを機械だけでなくシステムとして考えた場合、その中心になるのはOSです。ハードウェアとしてのコンピュータにとってはCPUが中心ですが、OSから見た場合は全てのハードウェアは資源になります。CPUも例外ではありません。 スレッドとはOSがCPU資源に処理を割り当てる最小単位である、と思ってください。ある瞬間に一つのCPUは一つのスレッドが占有します。 ちなみにスレッドとはunixやWindows系では10年弱くらい前から出てきた概念で、昔はLight Weight Process(軽量のプロセス)などと呼ばれていたこともありました。スレッド以前はCPU割り当ての最小単位はプロセスでした。プロセスとスレッドの違いは「専用のアドレス空間を持つか否か」です。 プロセスが生成されるとOSはそのプロセス専用のプロセス空間(アドレス空間)を生成します。その空間用に新たにページ変換テーブルやセグメント変換テーブルといったアドレス空間制御用のテーブルなどの生成も必要になり、それ相応のオーバーヘッドが発生します。 スレッドの場合は独自の空間は持ちません。そのスレッドを生成した親にあたるプロセスのプロセス空間内で動きます。そのためアドレス空間生成にかかるオーバーヘッドは無く、そういう面で軽量プロセスと言われてきました。 スレッドの概念に該当するものは汎用機では30年程前からあり(タスク自体はもっと前からですが、アドレス空間との絡みからいえばMVS(多重仮想記憶)になったS/370以降となるので約30年です)、タスクがこれにあたります(厳密には汎用機の場合はタスクもしくはSRBになるんですが、まあいいでしょう)。 そしてプロセス、プロセス空間にあたるのがジョブ、ジョブ空間です(ジョブ空間も厳密にはSTCジョブやMOUNTジョブ空間、TSOユーザー空間、SUBMITジョブ空間の三つに分かれますが、まとめてジョブ空間とします)。一つのジョブ空間には一つの親にあたるユーザータスクがあり、そこから子タスクを生成していきます。やはりジョブ生成に比べると新規アドレス空間の生成が無い分、オーバーヘッドはかかりません。 JavaはJavaVM(仮想マシン)上で稼動し、それ自体がある程度OSの機能も包含しています。JavaVMはそれが動くホストOSの1プロセスとして動き、その内部でJavaのスレッドが複数動く、という仕組みです。
・3番目の実験の実行結果とそうなる理由(危険その1から派生)の実験の実行結果は概ね次のようになると思います(ブラウザ1側で二つ目のデータ表示がされた段階でブラウザ2の実行ボタンを押した場合)。
|