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;
  }

问题

基类在多线程 Servlet 环境中运行。

解决方案#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 设计……以及您需要的线程安全级别。在某些情况下,使某些东西成为线程安全的东西甚至是不切实际的。例如,阅读 javadocCollections.synchonizedList并了解它如何处理该iterator()方法。

于 2012-06-17T02:53:29.287 回答
1

解决方案#1 是危险的,因为它假定会话是线程安全的,但情况并非如此。有人可能在同一个会话中同时发出两个请求。

解决方案 #2 可以通过让您的基类实现来轻松实现Cloneable。然后它可以克隆自己,设置克隆的实例变量,然后调用super.execute(). 如果您认为更改设计以使您的基类成为适当的线程安全太难了,那么这可能是一个简单的出路。

于 2012-06-17T07:24:19.940 回答