通过 Java 静态方法写入 log4j2 日志时包括 ThreadContext - 它是线程安全的吗?
Including the ThreadContext when writing log4j2 logs via a Java Static method - Is it thread safe?
在 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_id
和 user_id
,因为这两个参数都持有相同的引用?那么就会很混乱。我应该改用非静态方法吗?
我确实回答了下面的类似问题
Are non-synchronised static methods thread safe if they don't modify static class variables?
但我需要澄清一下。
参数的定义仅用于调用方法。不同调用之间不共享参数,即使来自同一线程也是如此。
参数在几乎所有效果中都作为局部变量起作用。如果你这样做
public void myMethod(int i) {
或
public void myMethod() {
int i;
在这两种情况下,i
是一个只能在该方法调用范围内访问的变量。如果再次创建该方法,会创建一个不同的独立副本。
如果没有这种情况,您 link 的答案将毫无意义(事实上,几乎不可能有并发系统;假设您需要同步每个静态方法,如 Integer.parseInt(String)
)
简而言之
很安全。
说明
ThreadContext
是一个作用域为每个线程的映射。随便说一句,每个线程都有自己的map实例,值对其他线程是不可见的。有关线程局部变量主题的更多详细信息,请参阅 https://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html。设置 ThreadContext
的方法是否静态并不重要。
有一个警告:Java 应用程序 servers/web 服务器使用线程池并重用线程。这意味着一旦一个线程完成了它的 Web 请求,它就会被重新用于处理下一个请求。 ThreadContext
中的数据保留下来,对下一个请求有效。为下一个请求保留数据通常是不可取的。完成请求后,您应该清除 ThreadContext
:
try{
setLogParams(company_id, userId);
... // do your business logic
} finally {
clearLogParams(); // Something like ThreadContext.clear();
}
更新:我没有涵盖 static/unsynchronized 方法部分,但这里是答案:
您的方法没有将参数存储在您自己的存储中;而是将它们存储在 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;
}
}
您必须同步所有方法调用以保证一个接一个的操作,这将有效地将您的整个系统变成一个行为类似于单线程但具有多线程和大量缺点的系统。
静态方法在同步方面与静态变量无关。然而,在某些情况下,您可以调用行为不同的同步非静态方法,但这会使我们与您最初的问题相去甚远。
在 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_id
和 user_id
,因为这两个参数都持有相同的引用?那么就会很混乱。我应该改用非静态方法吗?
我确实回答了下面的类似问题
Are non-synchronised static methods thread safe if they don't modify static class variables?
但我需要澄清一下。
参数的定义仅用于调用方法。不同调用之间不共享参数,即使来自同一线程也是如此。
参数在几乎所有效果中都作为局部变量起作用。如果你这样做
public void myMethod(int i) {
或
public void myMethod() {
int i;
在这两种情况下,i
是一个只能在该方法调用范围内访问的变量。如果再次创建该方法,会创建一个不同的独立副本。
如果没有这种情况,您 link 的答案将毫无意义(事实上,几乎不可能有并发系统;假设您需要同步每个静态方法,如 Integer.parseInt(String)
)
简而言之
很安全。
说明
ThreadContext
是一个作用域为每个线程的映射。随便说一句,每个线程都有自己的map实例,值对其他线程是不可见的。有关线程局部变量主题的更多详细信息,请参阅 https://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html。设置 ThreadContext
的方法是否静态并不重要。
有一个警告:Java 应用程序 servers/web 服务器使用线程池并重用线程。这意味着一旦一个线程完成了它的 Web 请求,它就会被重新用于处理下一个请求。 ThreadContext
中的数据保留下来,对下一个请求有效。为下一个请求保留数据通常是不可取的。完成请求后,您应该清除 ThreadContext
:
try{
setLogParams(company_id, userId);
... // do your business logic
} finally {
clearLogParams(); // Something like ThreadContext.clear();
}
更新:我没有涵盖 static/unsynchronized 方法部分,但这里是答案:
您的方法没有将参数存储在您自己的存储中;而是将它们存储在 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;
}
}
您必须同步所有方法调用以保证一个接一个的操作,这将有效地将您的整个系统变成一个行为类似于单线程但具有多线程和大量缺点的系统。
静态方法在同步方面与静态变量无关。然而,在某些情况下,您可以调用行为不同的同步非静态方法,但这会使我们与您最初的问题相去甚远。