Webアプリケーションは、「複数のユーザーが同時に利用する」ことがふつうです。
Tomcat等のWebアプリケーションコンテナは、複数のユーザーが処理を同時に実行できるように、マルチスレッドで並列に処理を実行します。
当然ですが、このコンテナ内で動作するサーブレットはマルチスレッドで実行されるため、他のユーザーが処理している内容に干渉しないようにする必要があります。
Java サーブレットのメンバ変数を使ってはいけないサンプル
ブラウザより、xとyの引数を受けて、足し算した結果をブラウザに返します。
計算した結果は、サーブレットのメンバ変数に格納しておきます。
また、同時実行しやすいように、5秒間処理を待たせてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MemberTestServlet extends HttpServlet { private int z = 0; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //ここで計算 z = Integer.parseInt(request.getParameter("x")) + Integer.parseInt(request.getParameter("y")); //同時に実行しやすいように5秒待つ try { Thread.sleep(5000); } catch (InterruptedException e) { } //結果をブラウザへ StringBuilder sb = new StringBuilder(); sb.append("<html><body>").append(z).append("</body></html>"); try(PrintWriter pw = response.getWriter()) { pw.write(sb.toString()); pw.flush(); } } } |
実行結果
2つのブラウザより、ほぼ同時(5秒以内)にアクセスします。
ブラウザ1つ目 : x = 1 , y = 2
ブラウザ2つ目 : x = 2 , y = 2
予想に反して(?)、どちらのブラウザでも2つ目に実行した値が返ってきました(; ・`д・´)
簡単な足し算なのにコンピュータでも間違えるんですねw
サンプルの解説
サーブレットのインスタンスは、スレッドごとに複数生成されません。
つまり、どちらのブラウザも同じインスタンスを利用して実行されていたため、メンバ変数の値が共有されてしまい、後から実行されたブラウザ2つ目の計算結果が上書きしてしまったのです。
※もちろん、使い方によっては、サーブレットでメンバ変数を使って良いのですが、ありがちな例です。
サンプルの対策
大きく2つあります。
- メンバ変数をやめて、doGetメソッド内のローカル変数で扱う。
- サーブレットでは処理をせず、別のクラスにメンバ変数・処理を移植。別のクラスをnewして使う。