使用ThreadLocal的最佳方法是什么 - 通过静态或非静态方法?

时间:2014-03-05 00:14:33

标签: java thread-local

对于我的应用程序,我必须将某些信息携带到应用程序的每一层(一个示例可能是为传入请求生成的唯一事务ID)。

所以,我打算创建一个引用ThreadLocal<ConcurrentMap<String, Object>>的类。

感谢有人可以提供帮助;我有一个混乱,出于以下方法,哪个更好:

1。具有静态方法的AppContext.java

public final class AppContext {
  private static final ThreadLocal&lt;ConcurrentMap&lt;String, Object&gt;&gt; context = new ThreadLocal<>();

  public static void set(final String key, final Object value) {
    // set key value in thread local
  }

  public static Object get(final String key) {
    // get value from thread local
  }
}

2。具有非静态方法的AppContext.java

public final class AppContext {
  private static final ThreadLocal&lt;ConcurrentMap&lt;String, Object&gt;&gt; context = new ThreadLocal<>();

  public void set(final String key, final Object value) {
    // set key value in thread local
  }

  public Object get(final String key) {
    // get value from thread local
  }
}

3 个答案:

答案 0 :(得分:7)

尽管其他人可能会说,ThreadLocal变量本身并不是邪恶的,它们在使用时只需要一点额外的关心和理解。线程安全并不真正相关,因为它们固有地绑定到一个线程。当这些线程可能被应用程序的其他不同用户重用时,就会引起关注。在基于Java的Web应用程序中,情况就是如此;但是,您一次只能依靠该线程上的一个用户/请求。非常重要的一步是确保在每个请求结束时清理ThreadLocal对象。

因此,为了避免不必要地为每个请求实例化新的上下文,我建议创建多个静态类型安全的ThreadLocal对象,所有这些对象都存在于“上下文”类中,其中这些对象以某种方式相关。您可以使用多个上下文类来组织这些上下文项的各种组。

然而,根据ThreadLocal评论家的精神,我同意ThreadLocals应该稍微谨慎使用,过度使用可能会产生代码味道。

以下是上述方法的一个例子。

“上下文”类。

public class LogContext {

    private static ThreadLocal<String> localCorrelationId = new ThreadLocal<String>();
    private static ThreadLocal<String> localUserId = new ThreadLocal<String>();

    public static String getCorrelationId() {
        return localCorrelationId.get();
    }

    public static void setCorrelationId(String correlationId) {
        localCorrelationId.set(correlationId);
    }

    public static String getUserId() {
        return localUserId.get();
    }

    public static void setUserId(String userId) {
        localUserId.set(userId);
    }

    public static void cleanup() {
        localCorrelationId.remove();
        localUserId.remove();
    }
}

通过Servlet过滤器管理上下文

@WebFilter(filterName = "LogContextFilter", urlPatterns = {"/*"})
public class LogContextFilter implements Filter {

    public void init(FilterConfig filterConfig) {

    }

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain)
            throws IOException, ServletException {

        try {
            LogContext.setCorrelationId(UUID.randomUUID().toString());
            chain.doFilter(request, response);
        } catch (Throwable t) {
            t.printStackTrace();
        } finally {
            //This is critical!
            LogContext.cleanup();
        }

    }

    public void destroy() {

    }

}

访问上下文(例如,来自Servlet)

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

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

        String correlationId = LogContext.getCorrelationId();
    }

}

答案 1 :(得分:1)

如果AppContext是单身,那么静态方法是最好的方法。如果有多个AppContext,那么您应该使这些方法是非静态的。

答案 2 :(得分:-1)

没有最好的方法来使用ThreadLocal。把它想象成“全局变量”,一切都是消极的。 ThreadLocal是地球上的一个痘。你的应用程序中的癌症,总有一天会消耗你。不惜一切代价避免它。学习如何传递适当的不可变状态,让你的线程难题消失。