InheritableThreadLocal的坑

原文:http://www.cnblogs.com/mvii/p/5646424.html

 

想必大家对ThreadLocal都比较熟悉,对于其子类InheritableThreadLocal,一看名字大概也能知道是干嘛的了。

不过绝不能仅仅从类名上想当然地认为他和ThreadLocal有相同的特性。我对InheritableThreadLocal的理解原来一直有问题,那就它居然不是线程安全的!好大一个坑啊。

看类的javadoc并没有很明显的提示,而且屡次提到values,实际却是references:

 * This class extends <tt>ThreadLocal</tt> to provide inheritance of values
 * from parent thread to child thread: when a child thread is created, the
 * child receives initial values for all inheritable thread-local variables
 * for which the parent has values.  Normally the child's values will be
 * identical to the parent's; however, the child's value can be made an
 * arbitrary function of the parent's by overriding the <tt>childValue</tt>
 * method in this class.

 不过从childValue这个方法的注释上能看出问题:

     * This method merely returns its input argument, and should be overridden
     * if a different behavior is desired.

 直接就是返回输入参数(的引用)

 

没看源码之前,我曾一度怀疑自己的三观。百度毫无结果,google之后找到一个帖子说明这个问题,我才找了源码进行查证,最终印证了这个老外的话。

You have to consider InheritableThreadLocal as Parent's Thread Local. When you get() or store() on an InheritableThreadLocal you are get() and store()ing the Parent thread's object. This can be modified by subclassing InheritableThreadPool and overriding the childValue method to make a ThreadLocal copy if you want children to see snap-shots of the parent value without ability to modify it.

Typically, though, you should not be using InheritableThreadLocal objects on anything that can change because it is not thread-safe.

归结为一句话就是,InheritableThreadLocal不是线程安全的。

 

那么如果有的应用场景需要它线程安全,该怎么做呢?附上我的部分代码供大家参考:

private static final ThreadLocal<GToadContext> gtoadContext = new InheritableThreadLocal<GToadContext>() {
        @Override
        protected GToadContext initialValue() {
            logger.trace("ThreadLocal GToadContext is initialized");
            return new GToadContext();
        }

        @Override
        protected GToadContext childValue(GToadContext parentValue) {
            logger.trace("ThreadLocal childValue is initialized, parent: {}", parentValue);
            GToadContext gToadContext = initialValue();
            gToadContext.setDataSource(parentValue.getDataSource());
            gToadContext.setUserId(parentValue.getUserId());
            gToadContext.setMerId(parentValue.getSubMerId());
            return gToadContext;
        }
    };

 其中GToadContext是我自己定义的,可以不用关注。主要是要重写childValue这个方法,将输入参数“parentValue”进行复制。

 

posted on 2016-08-22 16:28  malcolmshen  阅读(2639)  评论(0)    收藏  举报

导航