仕事で「スレッドセーフなサーブレット」の話に遭遇したのでまとめておきます。


通常のサーブレットは java.servlet.http.HttpServlet を拡張して作ります。この方法で作成した場合、サーブレットはシングルインスタンスがマルチスレッドで動作します。サーブレットのインスタンスそのものは1つで、この1つのインスタンスが複数のリクエストを並行処理する、という挙動になります。並行処理している間はスレッドセーフではありません:

import javax.servlet.http.*;

public class MyServlet extends HttpServlet{
  :
  :
}

仮にこのサーブレットがリクエストされ、その実行が終了する前に別のリクエストを受けた場合、別のリクエストは前のサーブレットの終了を待たずに実行が始まります。大半のケースではこれが期待する挙動になると思っています。 ただしスレッドセーフではないので、例えばサーブレットの処理の中にデータベースへの更新処理が含まれているような場合、排他制御できるかどうかはそのデータベースシステム側に求められるか、データベース側が排他制御していないのであればサーブレットの中に独自の排他制御の仕組みを実装する必要があります。

ではサーブレットをスレッドセーフに作る方法はないのか? というのが今回の話題でした。

その結論がこちらです。javax.servlet.SingleThreadModel インターフェースを継承して作成したサーブレットはシングルスレッドモードになり、実行中は他のスレッドリクエストは待ち行列に入ります。結果としてスレッドセーフな処理が実現できますが、処理速度を考えるとこのサーブレットで実現する部分は可能な限り小さく作るべきであると思っています:

import javax.servlet.http.*;
import javax.servlet.*; public class MyServlet extends HttpServlet implements SingleThreadModel{ : : }

実際の処理ではデータベースだけではなく、どのリソースにスレッドセーフ性が求められるのか、を意識して実装する必要があるのですが、そこが明確になっているのであれば上記のような方法で実装そのものは簡単にできる、ということになります。後はそこだけを綺麗に分離して、小さく作れるかどうか、、ということになるのかな。