java如何获取一个线程应用_Java多线程(一)-了解ThreadLocal

注意:本篇有因代码均为截图,如果喜欢看代码块请移步https://blog.csdn.net/qq_23633427/article/details/82054252

一、什么是ThreadLocal

ThreadLocal被大多数人叫线程本地变量,ThreadLocal为变量在每个线程中都创建了一个副本,每个线程可以访问自己内部的副本变量。

二、使用ThreadLocal与ThreadLocal是如何实现的

下面看一段代码例子,图2-1。

fed04d2ecbd5c84dbc459c96822fff15.png图2-1

如果stringThreadLocal就是正常的String变量的话在第一次输出的时候应该输出

线程id:1,线程名:main

第二三次输出的时候应该输出的都是新创建线程的线程id与线程名

那么我们来看一下实际结果

线程id:1,线程名:main

线程id:12,线程名:Thread-0

线程id:1,线程名:main

执行后我们发现第三次输出的还是主线程的id与线程名,也就是说ThreadLocal起到了作用,他在新的线程中创建了一个变量副本,而没有去修改主线程中的变量。

那么ThreadLocal是如何做到的呢,首先我们先看一下ThreadLocal提供的方法。

//get()是用来获取ThreadLocal在当前线程中保存的副本

public T get() { }

//set() 是用来设置当前线程中的变量副本

public void set(T value) { }

//remove() 用来清除当前线程中的变量副本

public void remove() { }

// initialValue() 这个方法是需要使用的时候实现,具体的作用会在讲解get方法时说到

protected T initialValue() { }

首先我们先来看看get()方法的实现,图2-2。

5c813edbdc2a75c38c7f88ffc6221f35.png图2-2

这段代码首先获取了当前线程,随后调用了一个叫做getMap()的方法,获取类一个类型为ThreadLocalMap的Map,随后通过传入this,获取到键值对,如果成功获取到键值对则返回value,如果没有获取到map或键值对e都会去调用setInitialValue()方法。

在概括的说明了get方法之后,我们从上到下看一下get方法中所调用的方法都干了些什么。

首先是getMap(),我们去看一下ThreadLocalMap是从哪里获得的,图2-3。79104723e5e9a465bd4baaa9a49b132d.png图2-3

令人震惊的是他直接返回了当前线程的一个成员变量threadLocals,那我们再去看一下Thread类中的成员变量,图2-4。

269e19578134da42b777560928ac8280.png图2-4

在查看了成员变量threadLocals后我们发现Thread中的成员变量threadLocals就是一个ThreadLocalMap,而这个类型是一个ThreadLocal的内部类。

ThreadLocalMap的Entry继承了WeakReference,并使用了ThreadLocal作为键值。

那么看完了getMap(),我们继续来看setInitialValue(),图2-5

8ef69469d37ecf433f7022182de954ef.png图2-5

这里就用到了之前说过的需要自己进行实现的方法initialValue(),咱们先不管他只需要知道他给我们返回了一个泛型T就可以了,之后的步骤就很容易了解了,与之前说的一样先去获得当前线程然后getMap(),判断map是否为null,如果为null创建并存入一个一个键值对,如果不为null,直接向map中存入一套键值对。

看完整个setInitialValue()方法,大家会发现initialValue()返回的value实际上就是存入键值对时的value,那我们去看一下没有重写的情况下initialValue()返回的是什么,图2-6。c02ba2ff2c6ff027209eeaf5b8ef4750.png图2-6

没错!是null哒!(皮一下.jpg)

按照我们刚才的理解如果不事先进行set(),直接get()的话应该会返回给我们一个null,如果我们自己重写了initialValue()方法那么就会按照我们重写的方法返回给我们对应的对象,那现在我们来试一试。

首先我们去掉所有的set()执行一遍试试,按照预期的想法他应该会输出null,图2-7。94e28a824637cee2c4bea76b7870761a.png图2-7

下面看一下执行结果:

null

null

null

嗯,和预期的结果一样都是null,那下面我们来重写initialValue()方法试一下,图2-8。cdf53b83140b92f56eaf01ba5b293157.png图2-8

看一下执行结果:

线程id:1,线程名:main

线程id:12,线程名:Thread-0

线程id:1,线程名:main

和分析的结果一样,重写了initialValue()方法后,不需要我们去set()直接调用get()方法也能获取到我们想要的内容。

三、withInitial(Supplier extends S> supplier)

如果你不愿意去重写initialValue()方法,java8也为你提供了新的方案,通过函数式编程的方式来生成一个ThreadLocal,多说无益,直接修改一下我们的例子,图3-1。

35cc303f3f80df1cc1aeb1989e6c5089.png图3-1

修改后执行结果:

线程id:1,线程名:main

线程id:12,线程名:Thread-0

线程id:1,线程名:main

与之前的代码一致。

四、总结

通过ThreadLocal创建的副本是保存在每个Thread中自己的成员变量threadLocals中的。

ThreadLocalMap的键使用ThreadLocal对象是因为每个线程中可能有许多ThreadLocal变量。

如果不希望调用set()方法请重写initialValue()方法,或通过withInitial()创建ThreadLocal对象,否则直接调用get()方法返回为null。

如果想在get()之前不需要调用set()的话,必须重写initialValue()方法,或通过withInitial()创建ThreadLocal对象。

ps:不能直接贴代码段好烦.......,我为什么忽然间脑子一抽想在B站也发一版。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值