ThreadLocal介绍

ThreadLocal 能做什么

ThreadLocal 是 JDK 提供的一个工具类,它可以为每个使用它的线程创建一个线程本地的副本,从而能保证多个线程在访问时的安全问题。当多个线程在使用这个变量时,其实是在使用自己线程本地内存的变量,由于是线程级别的,因此就能完全避免多个线程访问时,资源竞争的安全问题。

ThreadLocal 的原理

要讲解 ThreadLocal 的原理,我们首先需要看 JDK 的源码,了解了 ThreadLocal 类的主要方法实现就能理解其原理了。使用 ThreadLocal 时主要就是调用它的两个方法即 get 方法和 set 方法,下面直接贴出他们的源码。

首先看 set 方法:

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

由于 ThreadLocal 是一个带泛型的类,所以在创建 ThreadLocal 对象时,需要指定对应的泛型而 set 方法的入参就是定义的泛型对象;
接下来看具体的逻辑:
1首先创建了一个当前调用 set 方法的线程实例对象;
2、调用 getMap 方法,入参为当前线程的实例对象,返回一个当前线程对象的 threadLocals 属性值,threadLocals 是Thread 类的一个属性,也就是说 Java 的所有线程对象都有这个属性,这个属性是一个 ThreadLocalMap 对象(ThreadLocalMap 其实就是一个简化版的 HashMap);
3、如果返回的 ThreadLocalMap 为 null(threadLocals 属性的默认值就是 null),则会创建一个 ThreadLocalMap 对象,会调用createMap 方法;
4、createMap 方法接收两个入参,一个为 当前调用线程的实例对象,一个为 firstValue(默认 firstValue 为 null)因此这个方法会创建一个 key 为当前调用线程实例,value 为当前 set 方法传入的值 的ThreadLocalMap 对象,并将这个对象赋值给当前线程实例的 threadLocals 属性;
总结:要想了解 set 方法,就需要了解 threadLocals 属性,简单理解它就是一个 key 为当前线程实例对象的 HashMap;set 方法就是把 set 的入参设置到这个 hashMap 的 value 中的过程;

接下来看 get 方法:

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();
    }
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;
    }

其实 get 方法和 set 方法一样,本质上都是操作当前线程实例的 threadLocals 属性,只是 get 方法是获取该属性的 value 值;
接下来看具体的逻辑:
1、前面的两个操作和 set 方法一样,获取当前调用线程实例的 threadLocals 属性值;
2、如果不为空则直接返回当前属性值的 value 值;
3、如果为空则为当前线程实例初始化一个 value 值为空的 threadLocals 属性(initialValue 方法的返回值为 null);

原理总结

1、通过以上源码介绍,其实 ThreadLocal 是一个带泛型的类,使用的时候就是调用它的 get 方法和它的 set 方法;
2、这两个方法的本质就是操作当前调用线程实例的 threadLocals 属性值;
3、threadLocals 属性是一个 Thread LocalMap 对象,这个对象就是一个简单的 HashMap,而它的 key 为当前线程的实例对象,value 为某个需要被共享的变量值;
4、由于这个值是放在当前线程实例的一个 Map 中,因此多个线程访问的时候是访问自己本地的变量,因此不可能有多线程安全访问的问题;

ThreadLocal 应用 Demo

先上代码

public static void main(String[] args) {
        ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
        stringThreadLocal.set("main threadLocal");
        System.out.println("main threadLocal value:"+stringThreadLocal.get());
        Thread threadA = new Thread(() -> {
            stringThreadLocal.set("threadA threadLocal");
            System.out.println("threadA threadLocal value:" + stringThreadLocal.get());
        });
        Thread threadB = new Thread(() -> {
            stringThreadLocal.set("threadB threadLocal");
            System.out.println("threadB threadLocal value:" + stringThreadLocal.get());
        });
        threadA.start();
        threadB.start();
    }

结果

main threadLocal value:main threadLocal
threadA threadLocal value:threadA threadLocal
threadB threadLocal value:threadB threadLocal

上面是对 ThreadLocal 最简单的使用,虽然对 ThreadLocal 来说是一个对象,并且是被三个线程共同访问,正常情况下由于多线程的问题一定会表现为结果不可预知;但是由于 ThreadLocal 的特性,是当前线程的本地副本设置属性值,因此不会出现多线程安全的问题;可以用它来记录一下上下文的一内容信息,或者用来设置一个日志的唯一标识 uuid 等,来标识当前线程的执行日志,方便查找问题。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343