【问题标题】:Servlet thread safety: instance variables vs. request scope attributes [duplicate]Servlet线程安全:实例变量与请求范围属性[重复]
【发布时间】:2018-01-13 13:43:11
【问题描述】:

这是示例代码,说明了实例变量和请求属性的用法:

    @WebServlet(name = "Upload", urlPatterns = {"/upload"})
    @MultipartConfig()
    public class Upload extends HttpServlet {

    private String txt;

    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       try{
        String txt2 = (String) request.getAttribute("txt2");
        //txt and txt2 variables are available for processing.
         ..........
          } finally {
        txt = null;//Prepare variable for next request.
       }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            .....
        request.setAttribute("txt2", someValue);
        //vs
        txt = someValue;

    processRequest(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            .....
     processRequest(request, response);
      }
    }

现在我知道实例变量不应该在 servlet 中声明,因为在并发请求之间共享同一个 servlet。但是请求属性呢?使用它们安全吗?

【问题讨论】:

  • 不确定您到底想要什么,但请记住所有请求共享同一个 servlet 对象,因此您不能使用私有字段,除非您希望该字段在所有正在运行的 servlet 之间共享。 (你没有)
  • 你的意思是,如果我在 servlet 代码中声明像 private String str; 这样的变量,多个并发会话可以访问它?
  • 是的,正是.......
  • 所以这是一个非常严重的问题,并且明确地回答了我的问题..
  • 请求是唯一的。 Ergo 它的请求属性集合也是如此。 Ergo 它们是线程安全的。

标签: java performance servlets


【解决方案1】:

不确定初始化变量是什么意思。您是指在 servlet 上还是在哪里?

根据您需要执行的操作,可能涉及您无法控制合同的 API(因为它们不是您的 API)。

例如,当请求被过滤器链处理时。也许一个过滤器对链中先前执行的过滤器设置的数据感兴趣。你没有办法在过滤器链合约上传递数据。请求属性可能是一个选项。使用 ThreadLocal 可能是另一回事,但它会影响性能并且并不总是合适的。

另一个例子,也许你将一个请求从一个 servlet 转发到另一个。同样,您将无法选择使用您定义的任何成员变量。

另外,如果您正在考虑在 servlet 上添加成员变量,请记住 Servlet 不是线程安全的。你会怎么做?同步请求处理,每个 servlet 只处理一个请求?

【讨论】:

  • 让我们这样想——只有一个 servlet,没有转发和其他过滤器。该页面有几个输入字段并使用 POST 方法寻址到同一个 servlet。也许在这里使用属性是不合逻辑的?仅限请求范围。
  • 我想我找到了答案here - 它是关于线程安全的,它指出 “您需要修复 servlet 代码,使请求或会话范围数据永远不会被分配为 servlet 的实例变量。” 并且:“在方法中声明的变量是线程安全的......”
  • 这就是我在评论末尾所说的。 servlet 不是线程安全的,因此您不能在不保护线程安全的情况下使用成员变量。这样做会破坏请求处理的可扩展性
  • 所以这意味着唯一的方法/正确的方法是使用请求范围属性而不是实例变量?
  • 您提供的示例代码根本不需要请求属性。只需更改 processRequest 的方法签名以允许更多参数,然后您可以通过局部变量解决该问题。如前所述,请求属性的主要原因是在处理您无法控制的 API 时传递信息,并且不公开任何其他传递参数的方式。
【解决方案2】:

您的 servlet 将提供数百个并发请求,每个请求都将使用相同的 servlet 共享实例,因此使用私有实例变量将导致不一致的行为。除非你同步对它的访问,否则会导致对这个私有字段的访问争用更大,从而减慢响应时间,产生瓶颈。

虽然在绑定到单个请求中存储属性可以避免此类问题,但它旨在克服与您类似的问题。

【讨论】:

  • 是的,我同意。但是请求范围属性呢?有什么限制吗?
  • 不确定您询问的是哪些限制。显然存在大小限制,因为此类元数据存储在请求中。
  • 只是好奇:为什么这个答案被接受而不是我的?它说的和我的基本一样,但后来做了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-02
  • 2011-10-26
  • 1970-01-01
  • 2010-11-14
  • 2013-06-12
  • 1970-01-01
  • 2010-10-17
相关资源
最近更新 更多