在实例变量中使用ThreadLocal [英] Using ThreadLocal in instance variables
问题描述
如果Java ThreadLocal
变量用作实例变量,是否会产生线程局部值(例如,在生成线程局部对象的方法中),或者它们必须始终是静态变量才能执行是吗?
Do Java ThreadLocal
variables produce thread-local values if they are used as instance variables (e.g., in a method that generates thread-local objects), or must they always be static to do so?
例如,假设有一个典型的场景,其中需要在单个静态初始化块中实例化多个昂贵的,用于初始化非线程安全的类的对象,并将其存储在单个类的静态变量中(例如, Map
数据结构中),从那时起,它被大量不同的线程用于密集处理.
As an example, assume a typical scenario where several, expensive to initialize objects of a class that is not thread-safe, need to be instantiated in a single static initialization block, stored in static variables of a single class (e.g., in a Map
data structure) and from then on used for intensive processing by numerous different threads.
为实现线程安全,显然必须传递每个静态对象的不同副本.例如,需要在不同线程之间安全使用的Java DateFormat
对象.
To achieve thread safety, obviously a different copy of each static object must be passed. For instance, Java DateFormat
objects that need to be safely used across different threads.
在网上可以找到的许多示例中,该方法似乎是分别声明每个ThreadLocal
变量,在initialValue()
方法中实例化新对象,然后使用get()
方法来检索本地线程实例.
In many examples one can find on the web, the approach seems to be declaring separately each ThreadLocal
variable, instantiate the new object in the initialValue()
method and then use the get()
method to retrieve a thread-local instance.
如果要创建数十个或数百个此类对象(每个对象都有其自己的初始化参数),则此方法效率不高.例如,许多SimpleDateFormat
对象各自具有不同的日期模式.
This approach is not very efficient if there are dozens or hundreds of such objects to be created, each with its own initialization parameters. For example, many SimpleDateFormat
objects with a different date pattern each.
如果对象的实例化可以在每次迭代中产生不同值的循环中完成,则在通过适当地初始化相应对象创建每个值之后,将需要一种用于生成线程局部实例的通用方法.
If the instantiation of the objects could be done in a loop which produces a different value in each iteration, a generic method for producing the thread-local instances would be needed, after each value is created by properly initializing the corresponding object.
基于上述内容,以下通用静态方法将不起作用,因为每次调用initialValue()都会产生相同的引用:
Based on the above, the following generic static method would not work, because the same reference is produced on every call to initialValue():
// Each value is an object initialized prior to calling getLocal(...)
public static final <T> T getLocal(final T value)
{
ThreadLocal<T> local = new ThreadLocal<T>()
{
@Override
protected T initialValue()
{
return value;
}
};
return local.get();
}
相反,需要一种用于在initialValue()内部创建新对象的机制.因此,唯一通用的方法可能是使用反射,其模式类似于
Instead, a mechanism for creating a new object inside initialValue() is needed. So, the only generic approach is probably using reflection, in a pattern similar to
private static final <T> T getLocal(
final Constructor<T> constructor, final Object[] initargs)
{
ThreadLocal<T> local = new ThreadLocal<T>()
{
@Override
protected T initialValue()
{
T value = null;
try // Null if the value object cannot be created
{
value = constructor.newInstance(initargs);
}
catch (Exception e)
{
}
return value;
}
};
return local.get();
}
然后,当然,有一个特定于类型的选项,其中可以只在循环中使用ThreadLocal
模式来声明每个变量.
Then, of course, there is the type-specific option, where one could just use the ThreadLocal
pattern in the loop for declaring each variable.
例如,对于DateFormat
,在单个静态初始化块中,可以执行
For example, in the case of DateFormat
, in a single, static initialization block, one could do
private static String[] patterns = ... // Get date patterns
private static DateFormat format;
public static Map<String, DateFormat> formats = new HashMap<String, DateFormat>();
static
{
for (final String pattern:patterns)
{
format = new ThreadLocal<DateFormat>()
{
@Override
protected DateFormat initialValue()
{
return new SimpleDateFormat(pattern);
}
}.get();
formats.put(pattern, format);
}
从那时起,每次将调用不同类在不同线程中读取formats
映射,以便调用存储在映射中的一个或多个DateFormat
对象的format()
或parse()
方法.
From then on, the formats
map will be read by different classes, across different threads, each time in order to invoke the format()
or parse()
method of one or more DateFormat
objects stored in the map.
上述任何一种方法在上述情况下是否有意义,或者ThreadLocal
声明是否应该是静态的?
Does any of the above approaches make sense for the case described, or should the ThreadLocal
declarations be static?
推荐答案
如果Java ThreadLocal变量用作实例变量,是否会产生线程局部值.
Do Java ThreadLocal variables produce thread-local values if they are used as instance variables.
是的,他们这样做.考虑一下:不是ThreadLocal
是静态的还是非静态的,只有对ThreadLocal
的引用是静态的还是不是静态的.对象本身看起来总是一样.
Yes, they do. Think about it: Not the ThreadLocal
is static or non-static, only the reference to the ThreadLocal
is static or not. The object itself looks always the same.
上述任何一种方法在上述情况下是否有意义,还是ThreadLocal声明应该是静态的?
Does any of the above approaches make sense for the case described, or should the ThreadLocal declarations be static?
不是真的.
示例:
[DateFormat] format = new ThreadLocal<DateFormat>()
{...}.get();
formats.put(pattern, format);
表示您总是 创建一个新的ThreadLocal
,立即调用get
并将结果(而不是ThreadLocal
)放入地图中.这意味着您既不会重用ThreadLocal
也不会重用格式本身.
means, that you always create a new ThreadLocal
, call get
immediately and put the result (not the ThreadLocal
) into a map. This means you neither reuse the ThreadLocal
nor the format itself.
因此,据我了解您的用例,您可能想要这样的东西:
so, as far as I understand your usecase you might want something like this:
public class XXX {
private final static Map<String, SimpleDateFormatThreadLocal> formatMap =
new HashMap<String, SimpleDateFormatThreadLocal>();
static {
String[] patterns = {"a", "b", "c"};
for(String pattern: patterns){
formatMap.put(pattern, new SimpleDateFormatThreadLocal(pattern));
}
}
private static class SimpleDateFormatThreadLocal extends ThreadLocal<SimpleDateFormat> {
private final String pattern;
public SimpleDateFormatThreadLocal(String pattern) {
this.pattern = pattern;
}
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(pattern);
}
}
}
示例用法如下:
public void run(){
String s = formatMap.get("a").get().format(new Date());
System.out.println(s);
}
在这里
- 重复使用
ThreadLocal
对象 - 重用
DateFormat
对象(当然是每个线程) - 避免创建某些线程中未使用的
DateFormat
.
- reuse the
ThreadLocal
objects - reuse the
DateFormat
objects (per thread of course) - avoid creating
DateFormat
s which are not used in some threads.
这篇关于在实例变量中使用ThreadLocal的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!