code up

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Servletがスレッドを生成してはいけないのか

今更ながらのネタですが、Apache Commons FileUpload/IOの動作を調べてる際にServlet 3.0の仕様書(servlet-3_0-mrel-spec.pdf)を読む機会があり、3.0でServletがスレッドを生成することについてどう規定されているのかを確認した。

Servletからスレッドを生成するのは非推奨。だけど・・・

Servlet 3.0から非同期処理(Asynchronous Processing)がサポートされた。つまり、サーブレットからスレッドを生成する手順が正式に定義された。詳しくは@ITなどで解説されている。そのページでも述べられているが、それまではServletあるいはFilter内でスレッドを生成することは非推奨であった。

3.0でもAsynchronous Processingを使用しないServletについては変わらず、スレッドを生成するのは非推奨である。

仕様書では

仕様書内でAsynchronous Processingを使用しないServletあるいはFilterがスレッドを生成することについてどう説明されているのかをいくつか引用して説明したい。訳はいつも通り適当。詳しくはServlet 3.0の仕様書を直接ご確認を、ダウンロードがいつも重たいですけど・・・。

1.2. What is a Servlet Container?

For example, high-end application servers may limit the creation of a Thread object to insure that other components of the container are not negatively impacted.

ハイエンドなアプリケーションサーバーはコンテナ内のコンポーネントが悪影響を受けないよう保証するために、スレッドの生成に制限をかけるかもしれない。

2.3.3.3 Asynchronous processing

(省略)

ここで非同期処理(Asynchronous Processing)について規定がされている。

2.3.3.4 Thread Safety

If a thread created by the application uses the container-managed objects, such as the request or response object, those objects must be accessed only within the object’s life cycle as defined in sections 3.10 and 5.6.

アプリケーションが生成したスレッドがrequestやresponseのようなコンテナが管理するオブジェクトを使った場合、これらのオブジェクトは、3.10(たぶん3.11 Lifetime of the Request Objectを指してる)や5.6(Lifetime of the Response Object)で定義されているそのオブジェクトのライフサイクル内でアクセスされなくてはならない。

15.2.2 Web Application Environment

env-entry
ejb-ref
ejb-local-ref
resource-ref
resource-env-ref
service-ref
message-destination-ref
persistence-context-ref
persistence-unit-ref

(略) This type of servlet container should support this behavior when performed on threads created by the developer, but are not currently required to do so. Such a requirement will be added in the next version of this specification. Developers are cautioned that depending on this capability for application-created threads is not recommended, as it is non-portable.

サーブレットコンテナは、開発者が生成したスレッドが、各種参照(*-ref)をlookupできるようサポートすべきである。しかし現在の仕様では必ずしも必須ではない。この要求仕様は次のバージョンの仕様に加えられるだろう。アプリケーションが生成したスレッドが参照をlookupすることに依存した実装を行うことは互換性を低くするため非推奨であることを開発者に警告する。

A.8 Changes Since Servlet 2.3

It is now a recommendation, instead of a requirement, that the reference to the request and response object should not be given to the object in other threads - based on the requirement from JSR-168. Warnings are added when the thread created by the application uses the objects managed by the container.(2.3.3.3)

JSR-168に基づき『requestおよびresponseオブジェクトへの参照を別のスレッドにいるオブジェクトに渡すべきではない』は、必須から推奨に変更になった。アプリケーションによって生成されたスレッドがコンテナ管理のオブジェクトを使用した際は、警告が表示される。

結局、Servletがスレッドを生成してはいけないのか

気をつけて作れば大丈夫でしょう、という理解です。Asynchronous Processingを使わずにスレッドを生成する場合には次の3点に注意する。

HttpServletRequest(ServletRequest)、HttpServletResponse(ServletResponse)、JNDIリソースのlookup等のコンテナ管理のオブジェクトをスレッドに参照させない
仕様書で繰り返し述べられていること。JDBCであればコンテナに頼らずにコネクションを作成したり、requestの属性値が必要であれば、ValueObject等コンテナ管理ではないオブジェクトで受け渡しを行う。例えば、response.getOutputStream()を別のスレッドに渡したりするとデッドロックや異なるOutputStreamを参照してしまうかもしれないので注意!それでもアクセスしたい場合はそのコンテナ管理オブジェクトのライフサイクル内で済ますこと!というのは面倒&難しそうなので、一切コンテナ管理オブジェクトは参照しない、とする方が楽
生成したスレッドは必ず終了させる
スレッドを生成し、そのスレッド内で無限ループ等をさせたままでそのWebアプリケーションを停止、あるいはアプリケーションサーバーを停止させようとしても正しく終了しない。ServletContextListenerインタフェースのcontextDestroyedなどで確実に終了させる必要がある
リクエストに応じてスレッドを生成するような作りとはしない
リクエスト数が増えて(スレッドがこれ以上作れないという意味の)java.lang.OutOfMemoryErrorが発生しないようにする。つまり、スレッドプールとかExecutorServiceを使うなどしてリソースを食いつぶさないように

参考に

したというか、きっかけを与えてくれたのは、Apache Commons FileUploadFileCleanerCleanupServletContextListenerをimplementsしたListenerである。このクラスがApache Commons IOFileCleaningTrackerを参照しており、この中でスレッドが生成されていたことに疑問を感じたために調査を行ったのでした。

Commons FileUploadが利用しているServletContextListener#contextInitializedがスレッドの生成を非推奨とする記述はサーブレットの仕様書にはないようだ。むしろ一般的(フォーラムとかを覗くと)には、ユーザースレッドを作成したい場合はこの場所(と破棄用にcontextDestroyed)がよく利用されているようだ。

Servlet 3.0(Tomcat 7等)をベースとしたWebアプリケーションについて言えば、

  • asyncSupported属性を持つ@WebServletアノテーションをつけたServlet
  • asyncSupported属性を持つ@WebFilterアノテーションをつけたFilter
  • ServletContextListener#contextInitialized/contextDestroyed

あたりがスレッドを扱う場所としてよさげ。

関連記事
タグ:Java Servlet
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。