在实例变量中使用ThreadLocal [英] Using ThreadLocal in instance variables

查看:212
本文介绍了在实例变量中使用ThreadLocal的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果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 DateFormats which are not used in some threads.

这篇关于在实例变量中使用ThreadLocal的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆