Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/344.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在实例变量中使用ThreadLocal_Java_Thread Safety_Thread Local_Thread Local Storage - Fatal编程技术网

Java 在实例变量中使用ThreadLocal

Java 在实例变量中使用ThreadLocal,java,thread-safety,thread-local,thread-local-storage,Java,Thread Safety,Thread Local,Thread Local Storage,如果JavaThreadLocal变量用作实例变量(例如,在生成线程本地对象的方法中),它们是否会生成线程本地值,或者它们必须始终是静态的才能生成线程本地值 例如,假设一个典型的场景,其中需要在单个静态初始化块中实例化多个非线程安全的类的对象,这些对象的初始化成本很高,存储在单个类的静态变量中(例如,在映射数据结构中)从那时起,由许多不同的线程进行密集处理 为了实现线程安全,显然必须传递每个静态对象的不同副本。例如,JavaDateFormat对象需要跨不同线程安全使用 在web上的许多示例中,

如果Java
ThreadLocal
变量用作实例变量(例如,在生成线程本地对象的方法中),它们是否会生成线程本地值,或者它们必须始终是静态的才能生成线程本地值

例如,假设一个典型的场景,其中需要在单个静态初始化块中实例化多个非线程安全的类的对象,这些对象的初始化成本很高,存储在单个类的静态变量中(例如,在
映射
数据结构中)从那时起,由许多不同的线程进行密集处理

为了实现线程安全,显然必须传递每个静态对象的不同副本。例如,Java
DateFormat
对象需要跨不同线程安全使用

在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
    对象(当然是每个线程)
  • 避免创建一些线程中未使用的
    DateFormat
    s

为了回答您的标题问题,
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();
    }
}