线程故事:Web应用程序中的ThreadLocal

本周,我花了一些合理的时间来消除Web应用程序中的所有ThreadLocal变量。 原因是他们造成了类加载器泄漏,我们不能再适当地取消部署我们的应用程序。 取消部署应用程序后,当GC根目录继续引用应用程序对象时,将发生类加载器泄漏。 如果取消部署后仍引用了应用程序对象,则无法对整个类加载器进行垃圾回收,因为考虑的对象引用了您的应用程序类文件,而应用程序类文件又引用了类加载器。 取消部署和重新部署几次后,这将导致OutOfMemoryError

ThreadLocal是一种经典的候选人,可以轻松在Web应用程序中创建类加载器泄漏。 服务器正在管理池中的线程。 这些线程的寿命比您的Web应用程序更长。 实际上,直到底层JVM死亡,它们才完全消失。 现在,如果将ThreadLocal放入引用类的对象的池线程中,则必须*小心。 您需要确保使用ThreadLocal.remove()再次删除此变量。 Web应用程序中的问题是:安全删除ThreadLocal变量的正确位置在哪里? 此外,您可能不想每次同事决定将另一个ThreadLocal添加到托管线程时都修改该“删除代码”。

我们围绕线程局部开发了一个包装器类,该类将所有线程局部变量保留在一个单独的ThreadLocal变量中。 这是代码。

public class ThreadLocalUtil {

    private final static ThreadLocal<ThreadVariables> THREAD_VARIABLES = new ThreadLocal<ThreadVariables>() {

        /**
         * @see java.lang.ThreadLocal#initialValue()
         */
        @Override
        protected ThreadVariables initialValue() {
            return new ThreadVariables();
        }
    };

    public static Object getThreadVariable(String name) {
        return THREAD_VARIABLES.get().get(name);
    }

    public static Object getThreadVariable(String name, InitialValue initialValue) {
        Object o = THREAD_VARIABLES.get().get(name);
        if (o == null) { 
            THREAD_VARIABLES.get().put(name, initialValue.create());
            return getThreadVariable(name);
        } else {
            return o;
        }
    }

    public static void setThreadVariable(String name, Object value) {
        THREAD_VARIABLES.get().put(name, value);
    }

    public static void destroy() {
        THREAD_VARIABLES.remove();
    }
}

public class ThreadVariables extends HashMap<String, Object> { }

public abstract class InitialValue {

    public abstract Object create();

}

实用程序类的优点是无需开发人员就可以单独管理线程局部变量的生命周期。 该类将所有线程局部变量放在一个变量映射中。 可以调用destroy()方法,在其中可以安全地删除Web应用程序中的所有线程本机。 在我们的例子中,这就是ServletRequestListener -> requestDestroyed()方法。 您还需要将finally块放置在其他位置。 典型的地方是HttpServletinit()doPost()doGet()方法附近。 完成请求或意外引发异常后,这可能会删除池工作线程中的所有线程本地。 有时会发生服务器的main线程泄漏线程局部变量的情况。 如果是这种情况,则需要找到正确的位置来调用ThreadLocalUtil -> destroy()方法。 为此,要弄清楚主线程实际上在哪里创建线程变量。 您可以使用调试器来做到这一点。

许多人建议出于多种原因而在Web应用程序中省略ThreadLocal 。 在池化线程环境中删除它们可能非常困难,以便您可以安全地取消部署应用程序。 ThreadLocal变量可能有用,但是在应用它们之前考虑其他技术是很公平的。 Web应用程序可以携带请求范围参数的替代方法是HttpServletRequest 。 许多Web框架允许通用的请求参数访问以及请求/会话属性访问,而无需与本地Servlet / Portlet API绑定。 同样,许多框架支持请求使用依赖项注入将作用域Bean注入到对象树中。 所有这些选项都满足大多数要求,因此在使用ThreadLocal之前应考虑这些选项。

参考:线程故事: JCG合作伙伴 Niklas的Web应用程序中的ThreadLocal。


翻译自: https://www.javacodegeeks.com/2012/05/threading-stories-threadlocal-in-web.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值