Do Java ThreadLocal
如果变量用作实例变量,则它们会生成线程局部值(例如,在生成线程本地对象的方法中),或者它们必须始终是静态的吗?
作为一个例子,假设一个典型的场景,其中几个初始化非线程安全类的对象的成本很高,需要在单个静态初始化块中实例化,存储在单个类的静态变量中(例如,在Map
数据结构),并从此用于许多不同线程的密集处理。
为了实现线程安全,显然必须传递每个静态对象的不同副本。例如,JavaDateFormat
需要在不同线程之间安全使用的对象。
在网上可以找到的许多示例中,该方法似乎是分别声明每个ThreadLocal
变量,实例化新对象initialValue()
方法,然后使用get()
方法来检索线程本地实例。
如果要创建数十或数百个这样的对象,并且每个对象都有自己的初始化参数,则这种方法不是很有效。例如,许多SimpleDateFormat
每个对象都有不同的日期模式。
如果对象的实例化可以在每次迭代中产生不同值的循环中完成,则在通过正确初始化相应对象来创建每个值之后,将需要一种用于生成线程本地实例的通用方法。
基于上述,以下通用静态方法将不起作用,因为每次调用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() 内创建新对象的机制。因此,唯一通用的方法可能是使用反射,其模式类似于
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
循环中用于声明每个变量的模式。
例如,在以下情况DateFormat
,在单个静态初始化块中,可以这样做
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
每次,map 都会被不同的类、不同的线程读取,以便调用format()
or parse()
一种或多种方法DateFormat
存储在地图中的对象。
上述任何方法对于所描述的情况是否有意义,或者应该ThreadLocal
声明是静态的吗?