【问题标题】:Why should I avoid using InheritableThreadLocal in servlets?为什么要避免在 servlet 中使用 InheritableThreadLocal?
【发布时间】:2013-01-24 10:01:26
【问题描述】:

我在我的Servlet 类中使用InheritableThreadLocal。这样它就可以从它的子线程中获得。在线程池执行器中使用InheritableThreadLocal 是不是很邪恶? .比如servlet线程池。

我的问题。

1) 为什么我们应该避免在 servlet 中使用InheritableThreadLocals

2) InheritableThreadLocal 中是否可能发生内存泄漏?

3) InheritableThreadLocal 有什么替代方案吗?

4) 如果线程被重用,threadlocal 中存储的值不会被清除会怎样?

我的实时场景

public class UserAccessFilter implements javax.servlet.Filter {

      static final InheritableThreadLocal<String> currentRequestURI = new InheritableThreadLocal<String>();

      public void  doFilter(ServletRequest req, ServletResponse resp , FilterChain fc) throws IOException, ServletException{
              String uri = request.getRequestURI();
              fc.doFilter(request, response);         
      }
}


public class MailServlet extends HttpServlet{

      @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String mailCount = req.getParameter("mailCount");

    if(mailCount != null && !"".equals(mailCount) && mailCount.matches("[0-9]+")){
        MailThread mailThread = new MailThread("xxx@gmail.com", generateToAddress(Integer.parseInt(mailCount))); //NO I18N
        Thread t = new Thread(mailThread);
        t.start();
    }

    resp.getWriter().println("Mail Servlet.............."); //NO I18N

}              

}

class MailThread implements Runnable{

private String from;
private String to;

public MailThread(String from , String to){
    this.from = from;
    this.to = to;
}


@Override
public void run() {
    sendMail();
}

    public void sendMail(){
        //I want this uri from child threads. I can't pass this value to this constructor.
        String uri = currentRequestURI.get();
        //Do Mail Operations
    }


}

过滤器 --> Servlet A --> 子线程 ---> 邮件线程(这里我得到的是过滤器中设置的值)。

【问题讨论】:

    标签: java tomcat servlets thread-safety


    【解决方案1】:

    为什么我们应该避免在 servlet 中使用 InheritableThreadLocals?

    它们代表了将信息从一个请求泄漏到另一个请求的潜在路径。 “问题”是请求由线程池处理。当请求完成时,线程处理的下一个请求很可能是针对不同用户的。但是,如果您忘记在完成第一个请求时清除线程本地状态,则它可能会被第二个请求使用。

    InheritableThreadLocal 中是否可能存在内存泄漏?

    是的……有点。如果我们假设工作池是有界的,那么任何线程的线程本地状态都有可能被覆盖,从而清除内存泄漏。在最坏的情况下,问题是有限的内存泄漏......受池中线程数的限制。

    信息泄露问题更令人担忧。

    InheritableThreadLocal 有什么替代方案吗?

    在请求或响应对象中设置属性更好。

    如果线程被重用会发生什么,threadlocal中存储的值不会被清除。

    它不会被清除。这就是问题所在!

    【讨论】:

    • 我正在重置filter上的值,那么这不是问题吗?
    • 这取决于该值何时重置...以及您是否可以保证它将始终重置。
    • UserAccessFilter 中是否缺少应该设置值的行?然后在 servlet 中创建线程时继承该值,之后请求线程可以将其设置为您想要的任何值,创建的线程所看到的值将不受影响。
    【解决方案2】:

    您的示例有效,MailThread继承currentRequestURI 创建时的值。

    但是UserAccessFilterjava.lang.InheritableThreadLocal 只是用来混淆代码试图做什么,这是邪恶的部分。

    更改MailThread 的构造函数有什么问题,以便您可以像这样从MailServlet 传递请求URI:

    MailThread mailThread = new MailThread("xxx@gmail.com", generateToAddress(Integer.parseInt(mailCount)), req.getRequestURI());
    

    那你就不需要本地线程了,不需要过滤器,代码更清晰。

    【讨论】:

    • 我无法将 requestURI 传递给MailThread。我在这里发布示例场景。
    • 为什么你可以将requestURI传递给MailThread,你在doGet方法中有HttpServletRequest
    • 是的,但我不能限制发送请求 uri 来发送电子邮件。它可以被其他成员使用。
    • “其他成员”是什么意思?
    • 您的同事将如何使用您的代码?如果他们的代码只是向您的 servlet 发送请求,那么 requestURI 将只是您的 servlet 的代码。如果他们要使用MailThread 类,那么依赖于他们的已经在本地线程中正确设置了requestURI。底线是总是会犯错误,像这样复杂的方案只会增加出错的可能性,这就是为什么在一天结束时,你有一个测试周期。
    猜你喜欢
    • 1970-01-01
    • 2021-06-25
    • 1970-01-01
    • 2013-08-19
    • 2010-12-23
    • 2021-04-05
    • 1970-01-01
    • 2013-07-05
    • 2018-11-13
    相关资源
    最近更新 更多