前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ThreadLocal解析与最佳实践

ThreadLocal解析与最佳实践

作者头像
LNAmp
发布2018-09-05 15:46:34
1.1K0
发布2018-09-05 15:46:34
举报

概述

用于同一个线程内的方法要共享某些变量或状态的时候,提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度

源码解读

源码的阅读主要集中在几个关键方法

构造函数

代码语言:javascript
复制
  /**
     * Creates a thread local variable.
     */
    public ThreadLocal() {
    }

可以看出,默认的构造函数什么都没有干,但如果需要设置初始值怎么办

initialValue()

代码语言:javascript
复制
    protected T initialValue() {
        return null;
    }

使用者可以通过继承ThreadLocal覆盖该方法来设置初始值,该值在第一次调用get()方法时被调用,该方法在整个ThreadLocal的生命周期中应该只对调用一次,除非用户显示地调用了remove(),然后又调用get()时会再次调用initialValue

get()

代码语言:javascript
复制
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

get方法中可以看出先得到当前线程的threadlocalmap,如果不存在该map(首次调用get()),则调用setInitialValue(),如果存在则得到当前Key对应的值

setInitialValue()

代码语言:javascript
复制
private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

先调用intialValue得到初始值,然后得到该线程对应的ThreadLocalMap,然后在Map中set初始值,如果没有ThreadLocalMap则创建,并设置当前TheadLocal初始值.从上可以看出,初始化的时候可能做两件事 1、已有map 则将ThreadLocal作为key,initialValue为value放入到map中

2、没有map 新建一个ThreadLocalMap,并将<key,value>放入其中

createMap()

代码语言:javascript
复制
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

set(T value)

代码语言:javascript
复制
   public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

和setInitialValue方法类似

remove()

代码语言:javascript
复制
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

比较重要的一个方法,将当前的threadlocal变量从map中移除。

tips

比较重要的一点是,ThreadLocal,Thread,ThreadLocalMap的设计 目前的设计是Thread中有ThreadLocalMap,Map中以ThreadLocal为key,这种设计非常的清晰,由于在ThreadLocalMap中ThreadLocal是以WeakReference的形式存在的,所以其引用链或如下所示,也会产生GC疑问:ThreadLocal被回收,但是map中的entry一直不能回收的问题。 所以引出了最佳实践问题

threadlocal的引用链

ThreadLocal引用链.png

最佳实践

最佳实践的方法参见google guava eventbus中对于ThreadLocal的使用

代码语言:javascript
复制
    private final ThreadLocal<Boolean> dispatching;
    
    this.dispatching = new ThreadLocal() {
                protected Boolean initialValue() {
                    return Boolean.valueOf(false);
                }
            }
    if(!((Boolean)this.dispatching.get()).booleanValue()) {
                this.dispatching.set(Boolean.valueOf(true));

                Dispatcher.PerThreadQueuedDispatcher.Event nextEvent;
                try {
                    while((nextEvent = (Dispatcher.PerThreadQueuedDispatcher.Event)queueForThread.poll()) != null) {
                        while(nextEvent.subscribers.hasNext()) {
                            ((Subscriber)nextEvent.subscribers.next()).dispatchEvent(nextEvent.event);
                        }
                    }
                } finally {
                    this.dispatching.remove();
                    this.queue.remove();
                }
            }
  • 采用匿名内部类赋初始值
  • 显式调用get()、set()
  • 在不用的时候显式地remove()掉
  • 对于显示的remove特别重要,因为这样可以避免entry不被GC的情况 如果为了避免ThreadLocal被GC,可以加强ThreadLocal的引用,将其声明成private static

致谢

本文参考了解密ThreadLocal,引用了文中的图片,感谢作者,侵删。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016.08.04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 源码解读
    • 构造函数
      • initialValue()
        • get()
          • setInitialValue()
            • createMap()
              • set(T value)
                • remove()
                • tips
                • 最佳实践
                • 致谢
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档