PNS*_*PNS 5 java thread-safety thread-local thread-local-storage
ThreadLocal如果Java 变量用作实例变量(例如,在生成线程局部对象的方法中),或者它们是否必须始终是静态的,那么Java 变量是否会产生线程局部值?
作为一个例子,假设一个典型的场景,其中初始化非线程安全的类的对象的几个昂贵,需要在单个静态初始化块中实例化,存储在单个类的静态变量中(例如,在Map数据中)结构)从那时起用于许多不同线程的密集处理.
为了实现线程安全,显然必须传递每个静态对象的不同副本.例如,DateFormat需要跨不同线程安全使用的Java 对象.
在网络上可以找到的许多示例中,方法似乎是分别声明每个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();
}
Run Code Online (Sandbox Code Playgroud)
相反,需要一种在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();
}
Run Code Online (Sandbox Code Playgroud)
然后,当然,还有特定于类型的选项,其中可以使用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);
}
Run Code Online (Sandbox Code Playgroud)
从此,该formats图将被由不同的类以调用读取,在不同的线程,每个时间format()或parse()一个或多个的方法DateFormat存储在所述地图对象.
上述任何方法是否适用于所描述的情况,或者ThreadLocal声明是否应该是静态的?
要回答标题问题,请ThreadLocal为每个线程提供该ThreadLocal实例的单独值.因此,如果您在不同的位置有两个实例,则每个线程在每个实例中都有单独的值.这就是为什么ThreadLocals经常是静态的; 如果您想要的是每个线程的变量的单独值,那么您只需要ThreadLocal在JVM中为该变量使用一个值.
AH的答案非常好,我会建议进一步修改它.看起来您可能想要控制调用代码中的日期格式,而不是映射的定义.您可以使用以下代码执行此操作:
public class DateFormatSupplier {
private static final Map<String, ThreadLocal<DateFormat>> localFormatsByPattern = new HashMap<String, ThreadLocal<DateFormat>>();
public static DateFormat getFormat(final String pattern) {
ThreadLocal<DateFormat> localFormat;
synchronized (localFormatsByPattern) {
localFormat = localFormatsByPattern.get(pattern);
if (localFormat == null) {
localFormat = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat(pattern);
}
};
localFormatsByPattern.put(pattern, localFormat);
}
}
return localFormat.get();
}
}
Run Code Online (Sandbox Code Playgroud)
你ThreadLocal懒洋洋地创造了什么.
如果Java ThreadLocal变量用作实例变量,它们是否会生成线程局部值.
是的,他们这样做.想想看:不是ThreadLocal是静态的还是非静态的,只有参考的ThreadLocal是静态或没有.对象本身看起来总是一样的.
上述任何方法是否适用于所描述的情况,或者ThreadLocal声明是否应该是静态的?
并不是的.
例:
[DateFormat] format = new ThreadLocal<DateFormat>()
{...}.get();
formats.put(pattern, format);
Run Code Online (Sandbox Code Playgroud)
意味着,您总是创建一个新的ThreadLocal,get立即调用并将结果(而不是ThreadLocal)放入地图中.这意味着您既不重用ThreadLocal也不重用格式本身.
所以,据我所知,你可能想要这样的东西:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
示例用法如下:
public void run(){
String s = formatMap.get("a").get().format(new Date());
System.out.println(s);
}
Run Code Online (Sandbox Code Playgroud)
在这里
ThreadLocal对象DateFormat对象(当然每个线程)DateFormat某些线程中未使用的s.| 归档时间: |
|
| 查看次数: |
8928 次 |
| 最近记录: |