通过Java静态方法编写log4j2日志时包含ThreadContext - 它是否是线程安全的?

时间:2015-07-03 08:28:49

标签: java web-services concurrency thread-safety log4j

在Web服务应用程序中,我使用静态方法设置log4j ThreadContext变量以进行日志记录,如下所示,

public static void setLogParams(String company_id, String userId) {
    ThreadContext.put("company_id_val", company_id);
    ThreadContext.put("user_id_val", userId);
}

每个Web服务请求最初都会调用上面的方法,然后loj4j Logger object将用于完成剩下的工作。以上给定的值每次都不相同,可能会改变请求请求。

我的问题:以上情况是否是线程安全的?不同的Web服务请求是否共享相同的company_iduser_id,因为这两个参数都持有相同的引用?那会很混乱。我应该使用非静态方法吗?

我确实经历了下面的类似问题

Are non-synchronised static methods thread safe if they don't modify static class variables?

但我需要澄清一下。

2 个答案:

答案 0 :(得分:6)

简而言之

安全。

解释

ThreadContext是一个范围限定于每个线程的映射。随便说一下,每个线程都有自己的映射实例,其他线程看不到这些值。有关线程本地主题的更多详细信息,请参阅https://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html。无论您设置ThreadContext的方法是否为静态,都无关紧要。

有一点需要注意:Java应用程序服务器/ Web服务器使用线程池和重用线程。这意味着一旦线程完成了它的Web请求,它就会被重用来处理下一个请求。 ThreadContext中的数据仍然存在,并且对下一个请求有效。保留下一个请求的数据通常是不可取的。在您完成请求后,您应该清除ThreadContext

try{
    setLogParams(company_id, userId);
    ... // do your business logic
} finally {
    clearLogParams(); // Something like ThreadContext.clear();
}

更新:我没有介绍静态/非同步的方法部分,但是这里的答案是:

您的方法不会将参数存储在您自己的存储中;而是将它们存储在ThreadContext中。参数(无论它们是静态还是非静态方法)在调用和线程之间不重叠。参数的生命周期以方法调用的结束结束(只要您不存储数据,但引用是不同的主题)。

如果您将自己的customer_id存储在静态变量中,则会遇到竞争条件:

class RequestDataHolder {
   // this is not thread safe since multiple threads access the same data
   public static String customer_id;

   public static void setLogParams(String company_id, String userId){
      RequestDataHolder.customer_id = customer_id;
   }
}

您必须同步所有方法调用以保证一对一操作,这将使您的整个系统有效地转变为一个行为类似于单线程但具有多个线程和大量缺点的系统。

静态方法与同步时的静态变量无关。但是有一些场景,你可以在这些场景中调用行为不同的同步非静态方法,但这会导致我们离你原来的问题太远。

答案 1 :(得分:2)

仅为调用方法定义参数。参数不在不同的调用之间共享,即使是来自同一个线程。

参数在几乎所有效果中都可以作为局部变量使用。如果你这样做

public void myMethod(int i) {

public void myMethod() {
  int i;

在两种情况下i都是只能在该方法调用范围内访问的变量。如果再次创建该方法,则会创建一个不同的独立副本

如果没有这种情况,你链接到的答案就没有意义了(事实上,并发系统几乎不可能;想象你需要同步每个静态方法,比如{{ 1}})