Java 在实例变量中使用ThreadLocal
如果JavaJava 在实例变量中使用ThreadLocal,java,thread-safety,thread-local,thread-local-storage,Java,Thread Safety,Thread Local,Thread Local Storage,如果JavaThreadLocal变量用作实例变量(例如,在生成线程本地对象的方法中),它们是否会生成线程本地值,或者它们必须始终是静态的才能生成线程本地值 例如,假设一个典型的场景,其中需要在单个静态初始化块中实例化多个非线程安全的类的对象,这些对象的初始化成本很高,存储在单个类的静态变量中(例如,在映射数据结构中)从那时起,由许多不同的线程进行密集处理 为了实现线程安全,显然必须传递每个静态对象的不同副本。例如,JavaDateFormat对象需要跨不同线程安全使用 在web上的许多示例中,
ThreadLocal
变量用作实例变量(例如,在生成线程本地对象的方法中),它们是否会生成线程本地值,或者它们必须始终是静态的才能生成线程本地值
例如,假设一个典型的场景,其中需要在单个静态初始化块中实例化多个非线程安全的类的对象,这些对象的初始化成本很高,存储在单个类的静态变量中(例如,在映射
数据结构中)从那时起,由许多不同的线程进行密集处理
为了实现线程安全,显然必须传递每个静态对象的不同副本。例如,JavaDateFormat
对象需要跨不同线程安全使用
在web上的许多示例中,这种方法似乎是分别声明每个ThreadLocal
变量,在initialValue()
方法中实例化新对象,然后使用get()
方法检索线程本地实例
如果要创建几十个或数百个这样的对象,每个对象都有自己的初始化参数,那么这种方法就不是很有效。例如,许多SimpleDateFormat
对象各自具有不同的日期模式
如果对象的实例化可以在一个循环中完成,该循环在每次迭代中生成不同的值,那么在通过正确初始化相应对象创建每个值之后,将需要一个生成线程本地实例的通用方法
基于上述情况,以下通用静态方法将不起作用,因为每次调用initialValue()时都会生成相同的引用:
从那时起,formats
映射将由不同的类跨不同的线程读取,每次都是为了调用映射中存储的一个或多个DateFormat
对象的format()
或parse()
方法
上述方法对所描述的情况有意义吗,或者ThreadLocal
声明应该是静态的吗?
如果Java线程局部变量用作实例变量,它们是否会生成线程局部值
是的,有。想想看:ThreadLocal
不是静态的或非静态的,只有对ThreadLocal
的引用是静态的或非静态的。对象本身看起来总是一样的
上述任何一种方法对所描述的情况有意义吗,或者ThreadLocal声明应该是静态的吗
不是真的
例如:
[DateFormat] format = new ThreadLocal<DateFormat>()
{...}.get();
formats.put(pattern, format);
示例用法如下所示:
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
- 避免创建一些线程中未使用的
sDateFormat
ThreadLocal
为每个线程提供了该ThreadLocal
实例的单独值。因此,如果在不同的位置有两个实例,则每个线程中都有单独的值。这就是为什么ThreadLocal
s通常是静态的;如果您只需要为每个线程的一个变量指定一个单独的值,那么JVM中该变量只需要一个ThreadLocal
非常好,我将建议对其进行进一步修改。看起来您可能希望在调用代码中控制日期格式,而不是在映射的定义中。您可以使用如下代码来实现:
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();
}
}
公共类DateFormatProvider{
私有静态最终映射localFormatsByPattern=new HashMap();
公共静态日期格式getFormat(最终字符串模式){
线程本地本地格式;
已同步(localFormatsByPattern){
localFormat=localFormatsByPattern.get(模式);
if(localFormat==null){
localFormat=new ThreadLocal(){
@凌驾
受保护的日期格式初始值(){
返回新的SimpleDataFormat(模式);
}
};
localFormatsByPattern.put(模式,localFormat);
}
}
返回localFormat.get();
}
}
在这里,您可以懒洋洋地创建
ThreadLocal
s。通过使用静态ThreadLocal,ThreadLocal中的缓存模式可能更高效。而不是你描述的另一种方式
但是,如果您真的需要使用TraceLoopes作为实例变量(有案例),请考虑以下内容:
当您将ThreadLocal实例初始化为非静态变量时,会出现ThreadLocal内存泄漏的一个问题。当包含该变量的对象被垃圾回收时,ThreadLocal的引用将保留在线程中。如果随后在某种循环中实例化并使用许多ThreadLocals,就会出现内存泄漏 我在netty的FastThreadLocal中遇到了这个问题(我想java ThreadLocal也应该有同样的问题)。我的解决方案是在ThreadLocal中使用弱引用映射值来解决此问题。这允许使用ThreadLocal变量作为实例变量,在释放持有对象时可以对其进行垃圾收集 此处是代码(可用于代替ThreadLocals):+1用于
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);
}
}
}
public void run(){
String s = formatMap.get("a").get().format(new Date());
System.out.println(s);
}
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();
}
}