Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

Servletのメンバー変数について

掲示板の皆様、お世話になっております。

HttpServletもしくはEventListenerのサブクラスで宣言したメンバー変数及び定数は、複数の接続ユーザーから参照される可能性がある。
まずはこの認識に間違いはないでしょうか?

その上で以下、僕の認識に誤りがないか、ご指摘をお願いします。

・HttpServletもしくはEventListenerのサブクラスの全てのメンバー変数及び定数は参照される。
・HttpServletもしくはEventListenerを継承していないクラスでも、static修飾子のついたメンバ変数及び定数は参照される。
・HttpServletもしくはEventListenerを継承していないクラスの、staticではないメンバ変数及び定数は、アクセス修飾子に関係なく参照されない。

6

回答

75940

閲覧

6件の回答

評価

30

>まずはこの認識に間違いはないでしょうか?
一般的には正しいが、確実にそうだとは言えない。

まず、この件は「HttpServletもしくはEventListenerのサブクラス」とは本質的には関係がない。
参照はJavaそのものの仕組みによるものであり、特定のクラスについてのものではない。
基本はそのクラスのインスタンスがどう生成されるかと、そのメンバがstaticかどうかだ。

1. 実行単位ごとに生成
 1-1.非static…実行単位ごとになる。
 1-2.static…すべての実行単位で参照される。
2. 常に1つだけ生成
 2-1.非static…すべての実行単位で参照される。
 2-2.static…すべての実行単位で参照される。

デフォルトでは、HttpServlet/EventListenerは2になっている。このためフィールドがstaticかどうかは関係なくすべての実行単位で参照される(最初の認識が「一般的には正しい」理由)。
ただ、実装や設定によっては1であることもあり、この場合staticかどうかで挙動が変わる。
そして何より、「HttpServletもしくはEventListenerを継承していない」のに2となっているクラスは多くのJavaアプリケーションで一般的に存在している。

なお、別の実行単位で生成したインスタンスを参照する場合もないわけではなく、こうなると1-1でさえ共有と言える。

評価

0

ご回答くださいまして誠にありがとうございます。
出来るだけ理解できればと思いますので、もう少々お付き合いいただければ幸いです。

>>そのクラスのインスタンスがどう生成されるか
例えばですが、doGetもしくはdoPostメソッド内でMyClassというクラスのインスタンスをnewして生成したとします。
MyClass内のpublicでstaticではないフィールド変数は別の実行単位から参照されますか?

>>実装や設定によっては1であることもあり
ConcurrentHashMapのキーをセッションIDにした場合などでしょうか?
現在のservletの仕様では、別の実行単位のセッションIDを取得する方法はないという認識です。
この方法で実行単位毎に別の実行単位から干渉されない値を持てるという事はないですか?

>>「HttpServletもしくはEventListenerを継承していない」のに2となっているクラス
HttpServletもしくはEventListenerを継承したクラスで宣言した非プリミティブ型の変数を、doGetもしくはdoPost内でnewしたインスタンスの引数に渡している場合でしょうか?
また、僕が知らないだけでHttpServletやEventListenerではなくても、servletではよく使うのに2に属するクラスやインターフェースがあるのかもしれないですね。

評価

30

>MyClass内のpublicでstaticではないフィールド変数は別の実行単位から参照されますか?
まずは、得られた情報から自分で考えるクセをつけよう。
考えた内容をその根拠とともに書けるかどうかがポイントになる。

>ConcurrentHashMapのキーをセッションIDにした場合などでしょうか?
いや違う。特定のServlet自体が複数生成されることもあるということ。

>また、僕が知らないだけでHttpServletやEventListenerではなくても、servletではよく使うのに2に属するクラスやインターフェースがあるのかもしれないですね。
根本的に勘違いしているように見える。1か2かを決めるのは実装者自身だよ。
クラスの設計も無関係ではないが、そのクラスをどう使うか。

評価

0

ご返信ありがとうございます。

>>考えた内容をその根拠とともに書けるかどうかがポイントになる。
その通りだと思いますので考えた根拠をば。

まず、servletは1つのコンテナにつき、インスタンスを1つだけ作り、複数のアクセスユーザーに共有させます。
リクエスト1つ毎にプロセスを立ち上げなくてよいようにです。
つまりはメンバー変数が共有されるということ。

対して、doGetやdoPostメソッドはアクセスユーザー毎に別のスレッドで処理されていると考えます。
でないと、AさんのリクエストへのレスポンスをBさんに返すなんて事になりかねないからです。

つまりはdoGetやdoPost内でローカル変数として、インスタンスを宣言、初期化した場合はスレッドが別であり、メモリの参照先がそれぞれ違うので、そのローカル変数を複数ユーザーで共有することはない。
そのローカル変数内のpublicなメンバー変数であってもです。

しかし、staticな変数は話が違います。
インスタンスの生成毎に別のメモリ番地に値を書き込む動的変数とは違い、一度だけメモリ番地に値を書き込む静的変数なので、どの処理単位からも同じメモリ番地が参照され共有されます。

>>いや違う。特定のServlet自体が複数生成されることもあるということ。
そういうことですね。

CGIのようにアクセスの都度プロセスを立ち上げた場合、アクセス人数が増えると処理が追い付かなくなります。
逆に全ての処理を1つのコンテナで処理しようとしても、オーバーヘッドの原因になる上に、1つのアクセスで引き起こされたエラーの影響がアプリケーション全体に波及してしまいます。

ならば、コンテナを複数用意することで、処理能力と可用性の両方にバランスよく対処できます。
なので、servletもそのような仕組みになっていると想像しています。

つまりはコンテナの数だけservletのインスタンスも生成され、ConcurrentHashMapもその分だけインスタンスが作られると。

>>根本的に勘違いしているように見える。1か2かを決めるのは実装者自身だよ。
クラスの設計も無関係ではないが、そのクラスをどう使うか。

2に属するクラスであっても、スレッドセーフに設計していれば実質的に1になる。
逆に本来1に属するクラスでも実装によってはいろんな実行単位に意図せず変数を書き換えられる変なWEBシステムになるということですね。

評価

0

全体的に対比させているAとBの対象性が…だが、とりあえず通常のServletに関してはちゃんと把握できてると思うのでそこはOK。

>CGIのようにアクセスの都度プロセスを立ち上げた場合、アクセス人数が増えると処理が追い付かなくなります。
プロセスを立ち上げる話はどこから。
特定のServletが、
Date a=new Date();Date b=new Date();
のaとbのように複数インスタンスになる(つまり1)「こともある」という話(多分世の中のアプリの99%以上は単一なんだろうけど)。

>2に属するクラスであっても、スレッドセーフに設計していれば実質的に1になる。
>逆に本来1に属するクラスでも実装によってはいろんな実行単位に意図せず変数を書き換えられる変なWEBシステムになるということですね。
マルチスレッドにおいてスレッドセーフはもちろん重要な要素だけど、今の論点とはレイヤーが違う。

なんらかの情報を、「意図的に」実行単位をまたいで共有させる場合もあり、
それを通常のServletと同様に単一のインスタンスをすべての実行単位から参照する(つまり2)ことで実現する、という話。

Servletという存在を、なにか特別視していないだろうか。
Servletはただそういう機能をもったJavaのクラスの1つで、インスタンス、あるいはスレッドとの関係という意味ではほかのどんなクラスとも変わらない。

評価

0

「コンテナ(プロセス)」ごとに
「スレッド」は複数
Servlet「インスタンス」は1つ(だが複数の場合がある)。

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。