- PR -

ServletのdoGet, doPostはsynchronizedが必須!?

投稿者投稿内容
Edosson
ぬし
会議室デビュー日: 2004/04/30
投稿数: 675
投稿日時: 2004-12-17 15:19
引用:

Hiro2004さんの書き込み (2004-12-17 15:03) より:

セッション変数の値ではなく、一覧から取得するセッション変数名です。
前述のコードでも、セッション変数の値は全く考慮していなくて、セッション変数名だけを処理することを考えています。

例えば、セッション変数の名前として"V1", "V2", "V3", "V4", "V5"があったとします(値はここでは触れません)。
「Enumeration names = session.getAttributeNames(); 」を実行してセッション変数名(セッション変数の値ではない)の一覧を取得した後
このEnumeration namesからセッション変数名をnextElement()で順次取り出している時、
"V1"の名前を取り出した直後に別のスレッドで「session.removeAttribute("V3")」が行なわれた場合、どうなるか、を考えています。


Enumerator#nextElement()を実行したとき、ConcurrentModificationExceptionが
投げられる場合がある、そのときにどう対処するのか、を決めて、
そのようにプログラムを行う、それだけのことだと思いますが。

重要なのはHiro2004さんが「どうするか」ですよ。
「どうなるか」は正常に動作するか、Exceptionが発生するか、2通りしかありません。

確かに、自分のあずかり知らないところで、システムが動いているのは、
最初は気持ちが悪いかも知れませんね。

ま、何がどのように問題なのかは、人それぞれですし。

[ メッセージ編集済み 編集者: Edosson 編集日時 2004-12-17 15:24 ]
Hiro2004
会議室デビュー日: 2004/12/17
投稿数: 4
投稿日時: 2004-12-17 15:50
引用:

かつのりさんの書き込み (2004-12-17 14:30) より:
通常セッションは各ユーザ単位で発行される物であって、
同一セッションに対して非同期で動くことはあまりありえないケースですよね。
(IEでCtrl+Nを押して同じ画面を2つ開く等・・・)

ならばHttpServletのserviceメソッドをオーバーライドして

コード:
public void service(HttpServletRequest req,HttpServletResponse res) throws 省略
    synchronized(req.getSession(true)){
        super.service(req,res);
    }
}


みたいに記述すれば、同一セッションでのみスレッドセーフになります。
この記述をしたサーブレットをスーパークラスとするか、
コントローラとして使用すれば
ある程度安全に運用は可能じゃないかなと思います。
同期コストは必要ですが、マルチスレッドで動くので
パフォーマンスも悪くないはずです。

(ただし一部のサーブレットコンテナでは
getSession().toString().intern()で同期を行わなければいけませんが。)




たしかにあまりありえないケースなのですが、実際にこれだと思われる現象が発生していて、理論的にも起こりえる以上、何か対策をとらなくてはと考えています。
(ほとんど発生しないから気にしないで、とは言えませんし)。

なるほど、HttpSessionではなくて、セッションIDでsyncする必要があるんですね。
毎回synchronizedブロックするのは(気分的に)ちょっと気がひけるのですが、同一セッションでなければ非同期なんだし、安全でしょうね・・・。
kito
ベテラン
会議室デビュー日: 2003/03/24
投稿数: 59
お住まい・勤務地: Osaka
投稿日時: 2004-12-18 02:03
引用:

Hiro2004さんの書き込み (2004-12-17 10:27) より:

ConcurrentModificationExceptionを避けるためには、上記のsessionを使ってsynchronizedをすればいいでしょう。
しかし、セッション変数の一覧を取得する処理は多数あります。




引用:

Hiro2004さんの書き込み (2004-12-17 15:03) より:

セッション変数の値ではなく、一覧から取得するセッション変数名です。
前述のコードでも、セッション変数の値は全く考慮していなくて、セッション変数名だけを処理することを考えています。

例えば、セッション変数の名前として"V1", "V2", "V3", "V4", "V5"があったとします(値はここでは触れません)。



そもそも「セッション変数名の一覧を取得する処理」というのが非常に特殊な処理(Reflection的な雰囲気)なのですが、
そんな処理が多数あるという設計がまずいのではないですか?
デバッグ目的を除いて、セッション変数名を得たところでそれ以上有用な処理は行えないと思います。

本来Mapを使うべきところで、Sessionを使ってしまっているのではないですか?これは誤った設計です。
MapをSessionに格納する形にすれば、(同期が必要な場合も)素直に同期処理を実装できます。

コード:
Map dataMap = (Map)session.getAttribute("dataMap");
synchronized (myDataMap) {
    doSomething(myDataMap);
}


スキルアップ/キャリアアップ(JOB@IT)