バックグラウンド
Apache Actionクラスはスレッドセーフではありません。ただし、これは、システム内の他のすべてのクラスが依存する基本クラスを実装した後にのみ実現されました。基本クラスは、いくつかのインスタンス変数を使用します。
private HttpServletRequest request;
private ArrayList inputParams = new ArrayList();
private Connection connection;
private String outputParameter;
private boolean exportEnabled;
幸いなことに、これらの変数のすべての使用法は、アクセサー メソッドを介して排他的に実行されます。例えば:
public boolean getExportEnabled() {
return this.exportEnabled;
}
public void setExportEnabled( boolean exportEnabled ) {
this.exportEnabled = exportEnabled;
}
問題
基本クラスは、マルチスレッド サーブレット環境で実行されています。
解決策 1
この問題を解決するために、セッションをキーにしたHashMapを使用することを考えていました。ただし、これにはすべてのメソッドと依存コードを書き直す必要があります。
private static HashMap sessionVariables = new HashMap();
public boolean getExportEnabled( HttpSession session ) {
return getSessionVariables().get( session.getId() + '.exportEnabled' );
}
public void setExportEnabled( boolean exportEnabled, HttpSession session ) {
getSessionVariables().put( session.getId() + '.exportEnabled', exportEnabled );
}
これは大変な作業であり、バグが発生する可能性があります。
解決策 2
基本クラスを「空の」クラスに変更できる場合があります。この空のクラスには、単一のメソッドがあります。
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response )
throws Exception {
// Instantiate "this" and forward the request?
}
ただし、インスタンス化する適切な基本クラスを認識している必要があります。または、呼び出しを処理するために、それ自体の新しいバージョンをインスタンス化する必要があります。
更新 #1
私は、Struts アーキテクチャが次のことを行うと信じています。
- Action サブクラスのインスタンスを作成します。
- すべてのリクエストで同じインスタンスを再利用します。
- 新しい接続を受信するときに (スレッド プールから) スレッドを取得します。
execute
スレッドから Action サブクラスを呼び出します。- 異なるスレッドを使用して複数の新しい接続を処理します。
オブジェクトの同じインスタンスで同じexecute
メソッドが呼び出され、サブクラスにインスタンス変数があるため、安全でない動作が発生します。
アップデート #2
次の解決策は問題を解決するようです:
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response ) throws Exception {
((MyClass)clone()).executeClone( mapping, form, request, response );
}
public ActionForward executeClone(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response ) throws Exception {
// Former "execute" method code goes here.
// ...
}
元のexecute
メソッドは に名前が変更されましたexecuteClone
。新しいexecute
実装は、現在のクラスのクローンを作成し、続いて を呼び出しますexecuteClone
。この最小限の侵襲的手法により、クラスをスレッドセーフにしながら、新しいバグの発生を回避できます。
質問
バグが入り込むリスクを最小限に抑えながら、コードをスレッドセーフにする最も信頼できる方法は何でしょうか?
ありがとうございました!