2

バックグラウンド

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 アーキテクチャが次のことを行うと信じています。

  1. Action サブクラスのインスタンスを作成します。
  2. すべてのリクエストで同じインスタンスを再利用します。
  3. 新しい接続を受信するときに (スレッド プールから) スレッドを取得します。
  4. executeスレッドから Action サブクラスを呼び出します。
  5. 異なるスレッドを使用して複数の新しい接続を処理します。

オブジェクトの同じインスタンスで同じ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。この最小限の侵襲的手法により、クラスをスレッドセーフにしながら、新しいバグの発生を回避できます。

質問

バグが入り込むリスクを最小限に抑えながら、コードをスレッドセーフにする最も信頼できる方法は何でしょうか?

ありがとうございました!

4

2 に答える 2

1

バグを導入するリスクを最小限に抑えながら、コードをスレッドセーフにする最も信頼できる方法は何でしょうか。

これに対する一般的な答えはありません。クラスをスレッドセーフにするために必要なことは、クラスの機能、そのAPI設計、および必要なスレッドセーフのレベルによって異なります。場合によっては、スレッドセーフなものを作成することさえ実用的ではありません。たとえば、javadocを読んでCollections.synchonizedList、メソッドの処理iterator()方法を確認してください。

于 2012-06-17T02:53:29.287 に答える
1

解決策 1 は、セッションがスレッドセーフであると想定しているため、危険です。これは必ずしもそうではありません。誰かが同じセッションで 2 つのリクエストを同時に行っている可能性があります。

解決策 2 は、基本クラスを実装することで簡単に実装できますCloneable。次に、自身のクローンを作成し、クローンのインスタンス変数を設定してから、 を呼び出しますsuper.execute()。基本クラスを適切にスレッドセーフにするために設計を変更するのが難しすぎると思う場合、これは簡単な方法かもしれません。

于 2012-06-17T07:24:19.940 に答える