セッション管理とスレッドモデル(JavaServlet/JSP編)
結論
JavaServletの場合、セッション内部でもマルチスレッド処理(平行処理)されているので、sessionオブジェクトへのアクセスは排他制御しないとダメかもしれない。
実験
親のjspでセッションを作成、その後、ko0.jspとko1.jspに対して同時にアクセスされるはずなので、その様子を見てみよう。
<%@ page session="true" %>
<html>
<head>
</head>
<body>
Hello<br>
SessionId id <%= session.getId() %><br>
<iframe src="ko0.jsp"></iframe><br><hr>
<iframe src="ko1.jsp"></iframe>
</body>
</html>
ko0.jspとko1.jspは同じ内容で、10秒間スリープするだけの処理
<%= "Hello<br>\n" %>
<%
java.util.Date date1 = new java.util.Date();
out.println("SessionID id " + session.getId() + "<br>\n");
out.println("Start is " + date1 + "<br>\n");
java.lang.Thread.sleep(10000);
java.util.Date date2 = new java.util.Date();
out.println(" End is " + date2 + "<br>\n");
%>
結果
ko0.jsp と ko1.jsp が同時処理されているのがわかる。
つまり・・・
sessionオブジェクトへのアクセスには排他制御しておいた方がいいかもしれない
ということで・・・
Session変数が複数スレッドでブッ壊れるようなコードでテストしてみる
<%@ page session="true" %>
<html>
<head>
</head>
<body>
Hello<br>
SessionId id <%= session.getId() %><br>
<table border="1">
<tr>
<td><iframe src="ko0a.jsp" height="500"></iframe></td>
<td><iframe src="ko1a.jsp" height="500"></iframe></td>
</tr>
</table>
</body>
</html>
ko0a.jspは +1 ずつ計算結果をセッション変数に格納するよ
最後に結果を出力して、10秒後に再度結果を出力するよ
<%= "Hello<br>\n" %>
<%
java.util.Date date1 = new java.util.Date();
out.println("SessionID id " + session.getId() + "<br>\n");
out.println("Start is " + date1 + "<br>\n");
int sum = 0;
for(int i=0;i<100;i++){
Integer sumI = (Integer)session.getAttribute("sum");
if(sumI == null){
sumI = 0;
}
sum = (int)sumI;
sum++;
session.setAttribute("sum", sum);
out.print(i);
out.print(" : ");
out.print(sum);
out.println("<br>");
}
int sum0 = (int)session.getAttribute("sum");
out.print("sum=");
out.print(sum0);
out.println("<br>");
java.lang.Thread.sleep(10000);
int sum1 = (int)session.getAttribute("sum");
out.print("sum=");
out.println(sum1);
out.println("<br>");
java.util.Date date2 = new java.util.Date();
out.println(" End is " + date2 + "<br>\n");
%>
ko1a.jspは -1 ずつ計算結果をセッション変数に格納するよ
最後に結果を出力して、10秒後に再度結果を出力するよ
<%= "Hello<br>\n" %>
<%
java.util.Date date1 = new java.util.Date();
out.println("SessionID id " + session.getId() + "<br>\n");
out.println("Start is " + date1 + "<br>\n");
int sum = 0;
for(int i=0;i<100;i++){
Integer sumI = (Integer)session.getAttribute("sum");
if(sumI == null){
sumI = 0;
}
sum = (int)sumI;
sum--;
session.setAttribute("sum", sum);
out.print(i);
out.print(" : ");
out.print(sum);
out.println("<br>");
}
int sum0 = (int)session.getAttribute("sum");
out.print("sum=");
out.print(sum0);
out.println("<br>");
java.lang.Thread.sleep(10000);
int sum1 = (int)session.getAttribute("sum");
out.print("sum=");
out.println(sum1);
out.println("<br>");
java.util.Date date2 = new java.util.Date();
out.println(" End is " + date2 + "<br>\n");
%>
ということで・・・結果
うーん、forブロック内部では排他制御している!?
左側の
- sum=100
- sum=0
という2行を見ると、左側(ko0a.jsp[sum++])の1⇒100を(ロックしつつ)実行後に、右側(ko1a.jsp[sum--])の100⇒0を実行したような感じに見える
ということで・・・パート2
Session変数が複数スレッドでブッ壊れるようなコードでテストしてみる Part2
<html>
<head>
</head>
<body>
Hello<br>
SessionId id <%= session.getId() %><br>
<table border="1">
<tr>
<td><iframe src="ko0b.jsp" height="500"></iframe></td>
<td><iframe src="ko1b.jsp" height="500"></iframe></td>
</tr>
</table>
</body>
</html>
<%= "Hello<br>\n" %>
<%
java.util.Date date1 = new java.util.Date();
out.println("SessionID id " + session.getId() + "<br>\n");
out.println("Start is " + date1 + "<br>\n");
int sum = 0;
for(int i=0;i<50;i++){
Integer sumI = (Integer)session.getAttribute("sum");
if(sumI == null){
sumI = 0;
}
sum = (int)sumI;
sum++;
session.setAttribute("sum", sum);
out.print(i);
out.print(" : ");
out.print(sum);
out.println("<br>");
java.lang.Thread.sleep(1000);
}
int sum0 = (int)session.getAttribute("sum");
out.print("sum=");
out.print(sum0);
out.println("<br>");
java.lang.Thread.sleep(10000);
int sum1 = (int)session.getAttribute("sum");
out.print("sum=");
out.println(sum1);
out.println("<br>");
java.util.Date date2 = new java.util.Date();
out.println(" End is " + date2 + "<br>\n");
%>
ko0b.jspは +1 ずつ計算結果をセッション変数に格納するよ
最後に結果を出力して、10秒後に再度結果を出力するよ
Part1との違いは
- forブロックの中に1秒間のsleepを入れたこと。
- 100回は多いので、50回に減らした。
<%= "Hello<br>\n" %>
<%
java.util.Date date1 = new java.util.Date();
out.println("SessionID id " + session.getId() + "<br>\n");
out.println("Start is " + date1 + "<br>\n");
int sum = 0;
for(int i=0;i<50;i++){
Integer sumI = (Integer)session.getAttribute("sum");
if(sumI == null){
sumI = 0;
}
sum = (int)sumI;
sum--;
session.setAttribute("sum", sum);
out.print(i);
out.print(" : ");
out.print(sum);
out.println("<br>");
java.lang.Thread.sleep(1000);
}
int sum0 = (int)session.getAttribute("sum");
out.print("sum=");
out.print(sum0);
out.println("<br>");
java.lang.Thread.sleep(10000);
int sum1 = (int)session.getAttribute("sum");
out.print("sum=");
out.println(sum1);
out.println("<br>");
java.util.Date date2 = new java.util.Date();
out.println(" End is " + date2 + "<br>\n");
%>
ko1b.jspは -1 ずつ計算結果をセッション変数に格納するよ
最後に結果を出力して、10秒後に再度結果を出力するよ
ということで・・・結果 パート2
排他していないので、値がめちゃくちゃになった・・・・ヨシ
フレームの下はこんな感じ
Session変数は、マルチスレッドからの排他制御をきちんと実装した方がいいかもしれない。
結論
セッション変数の処理には、排他制御については少しは気を付けたほうがいいぞ。
でも、session変数のためだけに、synchronized しちゃうと、全体をロックしてしまうので!? 実行効率が落ちてしまうような気がする。
HTTPセッションの内部だけを同期させる方法って、あるのかな!? (誰か教えて)