ThreadLocal剧集(一)

总述

????最近做了一个日志调用链路跟踪的项目,涉及到操作标识在线程和子线程,线程池以及远程调用之间的传递问题。最终采用了阿里开源的TransmittableThreadLocal插件(https://github.com/alibaba/transmittable-thread-local)完美解决。在分析源码以及中途修复bug的过程中,被ThreadLocal搞得晕头转向。好在静下心来细细啃了一下午,终于能理解各种ThreadLocal相关问题了。这里准备用博客记录下来。

关于弱引用

????要想了解ThreadLocal的底层原理首先就要了解弱引用。本篇不会详细介绍是强引用,啥是弱引用、软引用以及虚幻引用,有兴趣的同学可以自己百度。这里直接给出弱引用的简单代码说明:

Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);  // 对堆内存中对象建立一个弱引用
obj = null;                 // 去掉堆中对象的强引用
System.gc();
System.out.println(wf.get());       // 输出null

可以看到弱引用的作用就在于当堆内存中对象不存在强引用的时候,在下一次gc的时候可能会回收掉堆内存占用。

走进ThreadLocal

????了解了弱引用之后,其实就能够很好地理解ThreadLocal(题外话:其实这个ThreadLocal我摸索了好久才弄得比较透彻)。直接上代码:

????首先我们需要注意Thread类中的两个属性:

public class Thread implements Runnable {
    // ....

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    // ThreadLocal实际值的存储所在
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    // 后面要将的InheritableThreadLocal的实际值存储所在
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}

这两个属性特别关键:

  • 他是每个线程所特有的
  • 两个属性的类型是ThreadLocal的内部静态类

他们是ThreadLocal的神奇魔法之关键~

????接下来我们来看看ThreadLocal的关键方法:

/**
 * Sets the current thread's copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * override this method, relying solely on the {@link #initialValue}
 * method to set the values of thread-locals.
 *
 * @param value the value to be stored in the current thread's copy of
 *        this thread-local.
 */
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

/**
 * Create the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param t the current thread
 * @param firstValue value for the initial entry of the map
 */
void createMap(Thread t, T firstValue) {
    // set时候的关键,实际上是创建一个当前ThreadLocal的弱引用为key的Map
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

/**
 * Returns the value in the current thread's copy of this
 * thread-local variable.  If the variable has no value for the
 * current thread, it is first initialized to the value returned
 * by an invocation of the {@link #initialValue} method.
 *
 * @return the current thread's value of this thread-local
 */
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

/**
 * Get the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 * 最为关键的方法: 可以看出getMap实际上就是得到传入线程的threadLocals属性的值
 * @param  t the current thread
 * @return the map
 */
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

/**
* Removes the current thread's value for this thread-local
* variable.  If this thread-local variable is subsequently
* {@linkplain #get read} by the current thread, its value will be
* reinitialized by invoking its {@link #initialValue} method,
* unless its value is {@linkplain #set set} by the current thread
* in the interim.  This may result in multiple invocations of the
* {@code initialValue} method in the current thread.
*
* @since 1.5
*/
public void remove() {
 ThreadLocalMap m = getMap(Thread.currentThread());
 if (m != null)
     m.remove(this);
}

不难看出,所有方法都是围绕着一个ThreadLocalMap来操作的,那么这个ThreadLocalMap究竟是啥,我们进一步来分析:

/**
 * ThreadLocalMap is a customized hash map suitable only for
 * maintaining thread local values. No operations are exported
 * outside of the ThreadLocal class. The class is package private to
 * allow declaration of fields in class Thread.  To help deal with
 * very large and long-lived usages, the hash table entries use
 * WeakReferences for keys. However, since reference queues are not
 * used, stale entries are guaranteed to be removed only when
 * the table starts running out of space.
 */
static class ThreadLocalMap {

    /**
     * The entries in this hash map extend WeakReference, using
     * its main ref field as the key (which is always a
     * ThreadLocal object).  Note that null keys (i.e. entry.get()
     * == null) mean that the key is no longer referenced, so the
     * entry can be expunged from table.  Such entries are referred to
     * as "stale entries" in the code that follows.
     */
    // 实际上他的存储也是利用Entry结构来进行的,只不过这个Entry的key值是弱音用对象,实际上可以将ThreadLocalMap看做WeakHashMap
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
}

至此,我们已经知道了ThreadLocal是如何实现的了,具体来说是下面几个关键点:

  • ThreadLocal本身并不存储值,而是作为ThreadLocalMap的key用来查找对象所存储的值的
  • 用来存储值的ThreadLocalMap是每个线程都有的非静态属性,当前线程实例该属性的值对其他线程实例是不可见的,这也就实现了线程隔离
  • ThreadLocal的get方法实际上是先获取当前线程的ThreadLocalMap属性值,然后再通过ThreadLocal作为key获取实际上存储在Map中的值
  • 因为ThreadLocalMap的Key是软引用的,所以如果ThreadLocal不存在强引用且线程被回收的话,存储在已回收线程ThreadLocalMap中的值也是会被回收的。这一点是通过两方面来实现的:1. Key是软引用,当没有强引用指向ThreadLocal时,ThreadLocalMap的以该ThreadLocal作为key的Entry中key会在gc时被回收置为null 2. 调用ThreadLocal的set/get/remove方法的时候会触发Entry的expungeStaleEntry方法,方法会将key为null的value值回收

原文地址:https://www.cnblogs.com/Kidezyq/p/10462084.html

时间: 2024-10-15 21:58:30

ThreadLocal剧集(一)的相关文章

Scrapy爬取天天美剧封面照及剧集下载地址

其实我只是想试试爬取图片而已,先看看网页,需要爬的地方有两个,一是封面图,二是下载地址,挺简单的 Item定义: import scrapy class TiantianmeijuItem(scrapy.Item):     name = scrapy.Field()     image_urls = scrapy.Field()     images = scrapy.Field()     image_paths = scrapy.Field()     episode = scrapy.F

分布式改造剧集三:Ehcache分布式改造

第三集:分布式Ehcache缓存改造 前言 ? 好久没有写博客了,大有半途而废的趋势.忙不是借口,这个好习惯还是要继续坚持.前面我承诺的第一期的DIY分布式,是时候上终篇了---DIY分布式缓存. 探索之路 ? 在前面的文章中,我给大家大致说过项目背景:项目中的缓存使用的是Ehcache.因为前面使用Ehcache的应用就一台,所以这种单机的Ehcache并不会有什么问题.现在分布式部署之后,如果各个应用之间的缓存不能共享,那么其实各自就是一个孤岛.可能在一个业务跑下来,请求了不同的应用,结果在

android自定义控件系列教程-----仿新版优酷评论剧集卡片滑动控件

我们先来看看优酷的控件是怎么回事? 只响应最后也就是最顶部的卡片的点击事件,如果点击的不是最顶部的卡片那么就先把它放到最顶部,然后在移动到最前面来,反复如次. 知道了这几条那么我们就很好做了. 里面的技术细节可能就是child的放置到前面来的动画问题把. 先看看我们实现得效果: 然后仔细分析一下我们要实现怎么样的效果: 我也是放置了一个按钮和两个view在控件上面,只有当控件在最前面也就是最里面的时候才会响应事件. 然后我们就动手来实现这个控件. 我们继承一个ViewGroup并且命名为Exch

韩剧在中国:亿级爆款IP的套现之路

商业化元素更加丰富.结合度更高.且针对不同市场"故意"定制差异化IP.和当地市场的"合伙人"同步开发市场,将成为韩剧挖掘IP价值,实现套现最大化的重中之重. 文/张书乐 刊载于<法人>杂志2016年6月刊 不到两个月的时间,韩国神剧<太阳的后裔>(以下简称<太后>)持续保持话题之王的佳绩,不仅在视频网站上点播破4亿次,连"@公安部打四黑除四害"官微都发帖提示"韩剧迷小心!看<太阳的后裔>或

最新最全的 Android 开源项目合集

原文链接:https://github.com/opendigg/awesome-github-android-ui 在 Github 上做了一个很新的 Android 开发相关开源项目汇总,涉及到 Android 开发的方方面面,基本很全了.对 Android 开发感兴趣的欢迎 Star ,后续也会定期维护更新这个列表.当然,你也可以去 opendigg 上查看. -- 由欧戈分享 awesome-github-android-ui 是由OpenDigg整理并维护的安卓UI相关开源项目库集合.

Python爬虫爬取美剧网站

一直有爱看美剧的习惯,一方面锻炼一下英语听力,一方面打发一下时间.之前是能在视频网站上面在线看的,可是自从广电总局的限制令之后,进口的美剧英剧等貌似就不在像以前一样同步更新了.但是,作为一个宅diao的我又怎甘心没剧追呢,所以网上随便查了一下就找到一个能用迅雷下载的美剧下载网站[天天美剧],各种资源随便下载,最近迷上的BBC的高清纪录片,大自然美得不要不要的. 虽说找到了资源网站可以下载了,但是每次都要打开浏览器,输入网址,找到该美剧,然后点击链接才能下载.时间长了就觉得过程好繁琐,而且有时候网

&lt;转&gt; 纸牌屋1-4集分析

原文:http://blog.sina.com.cn/s/blog_b86c61490102v56t.html 第一季第一集 主人公弗兰克的出场,是以对待一只邻家将死之狗的态度展开的,充分显示了主人公的做事作风立场:(以小喻大)残忍,果断,决绝,对无用之物毫不怜悯,正是一个深通权谋的政客(不是政治家)应具有的品格.此时也出现了第一次弗兰克的镜头旁白,全剧多处充斥着主人公的镜头旁白,多为了解释弗兰克的性格作风等等,以及向不懂权谋术的观众解释权谋术的应用. 今夜弗兰克心情大好,亲呢地为妻子拉衣链赞美

人人影视 for Mac(美剧电影必备神器)

人人影视 Mac客户端是观看美剧电影必备的神器!人人影视 for Mac网络全网海量电影,电视,动漫,综艺等影视资源,无需会员即可观看!想要看各种美剧,日剧等海外影视资源,人人影视 Mac客户端将会是你最好的选择!最新人人影视Mac版软件界面由原来的浅灰色变成暗黑色,并且还新增预告墙这个功能,让你能够提前获取电影电视等资源详情! 免费下载人人影视 for Mac(美剧电影必备神器) 人人影视Mac版软件介绍 人人影视Mac版是人人影视在Mac平台上推出的一款影视下载工具软件,人人影视Mac版可一

简单爬虫一部美剧(二:下载进度条)

上篇获取到了每一集的下载url,这篇就用获取到的url下载视频 在下载文件时希望可以给出进度条之类的提示,在网上搜索了一波,发现有一个库可以实现:tqdm库,具体用法参考这篇博客:https://www.jianshu.com/p/1ed2a8b2c77b 在原来的类下面,再加一个方法,用来下载文件,如下 def download_file(url, name): """下载文件""" try: response = requests.get(u