博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Java面试题】ThreadLocal是什么?
阅读量:3758 次
发布时间:2019-05-22

本文共 5232 字,大约阅读时间需要 17 分钟。

这道题想考察什么?

  • 是否了解ThreadLocal的使用?
  • 是否了解ThreadLocal的原理?

考察的知识点

  • ThreadLocal的使用
  • ThreadLocal的原理

考生应该如何回答

1、首先,我们先简单介绍一下ThreadLocal的定义与使用。

ThreadLocal,线程本地变量,顾名思义,它是每个线程私有的本地变量。通俗点讲,当你创建了一个ThreadLocal变量,每个线程在访问该变量时,都会拷贝一个副本至本地内存,所以多线程下操作ThreadLocal变量时,其实各自都是在操作自己拷贝的副本,互不影响,这样自然而然就避免了线程安全问题。看个例子,可能就明白了。

public class ThreadLocalTest {
//定义一个ThreadLocal变量,泛型指定为Integer private ThreadLocal
threadLocal = new ThreadLocal
() {
@Override protected Integer initialValue() {
//初始值置为1 return 1; } }; @Test public void testThreadLocal() {
//创建线程thread1 MyThread thread1 = new MyThread("thread1"); //启动线程thread1 thread1.start(); //创建线程thread2 MyThread thread2 = new MyThread("thread2"); //启动线程thread2 thread2.start(); } class MyThread extends Thread {
MyThread(String name) {
super(name); } @Override public void run() {
super.run(); //获取threadLocal的值,并输出 System.out.println(getName() + "------ threadLocalValue is " + threadLocal.get()); //threadLocal的值 + 1,并设置到threadLocal threadLocal.set(threadLocal.get() + 1); //再次获取threadLocal的值,并输出 System.out.println(getName() + "------ after ++, threadLocalValue is " + threadLocal.get()); } }}

logcat输出如下

// thread1------ threadLocalValue is 1// thread1------ after ++, threadLocalValue is 2// thread2------ threadLocalValue is 1// thread2------ after ++, threadLocalValue is 2

例子很简单,开启两个线程去操作ThreadLocal变量,从控制台的输出结果,便可以证明我们上面说的定义,每个线程对threadLocal变量的访问与操作互不影响,做到了线程隔离。

2、如果只回答到怎么使用就结束了,显然不是一个程序员的追求。咱必须得吹吹原理呀,点进源码,看看ThreadLocal的实现原理到底是怎么一回事。

先从Thread类看起。

class Thread implements Runnable {
...//省略不相关代码 //每个线程内部有个ThreadLocalMap的私有成员变量 ThreadLocal.ThreadLocalMap threadLocals = null; ...//省略不相关代码 private void exit() {
... //线程退出时, threadLocals变量置空 threadLocals = null; ... } }

整个Thread类中只需要关注两点就行,第一点,每个Thread都会有一个ThreadLocalMap类型的threadLocals变量,第二点就是这个threadLocals会在线程退出时置空。至于啥时候往里面放东西,先不用急,后面自然会提到。

看看ThreadLocal的内部类ThreadLocalMap吧。

//ThreadLocalMap功能类似HashMap,内部维护了一个Entry数组进行存储数据

static class ThreadLocalMap {
//Entry是K-V形式的实体,key为ThreadLcal变量,value为任意类型的变量 static class Entry extends WeakReference
> {
//该value与ThreadLocal变量绑定 Object value; Entry(ThreadLocal
k, Object v) {
super(k); value = v; } } //内部维护的Entry数组 private Entry[] table; //构造方法 ThreadLocalMap(ThreadLocal
firstKey, Object firstValue) {
... //省略不相关代码 //初始化Entry数组 table = new Entry[INITIAL_CAPACITY]; ... //省略不相关代码 }}

ThreadLocalMap其实就是一个数据存储结构,类似HashMap。它内部也是维护了一个Entry实体数组,这个Entry是以K-V形式进行存储数据,key为ThreadLocal变量,value支持任意类型的变量,目前还不知道它具体存了什么,接着再往下看。

ThreadLocal本身终于要亮相了,看看ThreadLocal的核心方法setInitialValue / get / set 都做了啥。

public class ThreadLocal
{
//设置初始值 private T setInitialValue() {
//通过我们重写initialValue方法获取初始值 T value = initialValue(); //获取到当前线程 Thread t = Thread.currentThread(); //通过getMap方法,传入当前线程作为参数去获取ThreadLocalMap对象 ThreadLocalMap map = getMap(t); //如果获取到的ThreadLocalMap不为空,就直接存入,否则就创建ThreadLocalMap并存入。 if (map != null) //key为threadLocal本身,value就是我们业务上需要存储的值 map.set(this, value); else createMap(t, value); return value; } //获取ThreadLocalMap对象 ThreadLocalMap getMap(Thread t) {
//重点!!! //这里返回的其实就是最上面讲到的每个Thread都会有一个ThreadLocalMap成员变量 return t.threadLocals; } //获取ThreadLocal的value值 public T get() {
//获取当前线程 Thread t = Thread.currentThread(); //跟上面一样,拿取当前线程的ThreadLocalMap变量 ThreadLocalMap map = getMap(t); //到ThreadLocalMap中查找数据并返回 if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) {
@SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } //设置ThreadLocal的value值 public void set(T value) {
//获取当前线程 Thread t = Thread.currentThread(); //又是跟上面一样,拿取当前线程的ThreadLocalMap变量 ThreadLocalMap map = getMap(t); //存储数据 if (map != null) map.set(this, value); else createMap(t, value); }}

从ThreadLocal的源码可以看到,三个核心方法的思路其实都是如出一辙的。通过Thread.currentThread()获取当前线程,因为在最早分析Thread类时,我们就提到每个Thread都有一个自己的ThreadLocalMap成员变量,所以拿到当前线程便可以轻松访问到当前线程的ThreadLocalMap对象,那样存数据、取数据等操作直接由ThreadLocalMap完成就行了。因为每个线程拿到ThreadLocalMap对象不同,所以存储的数据做到了线程隔离,这也是ThreadLocal最为重要的地方。

3、上面源码理解了,ThreadLocal的原理也就没问题了,最后再附上一张图,稍微简单明了的总结一下。

在这里插入图片描述

每个线程都有一个自己的ThreadLocalMap对象,ThreadLocalMap对象里维护了一个K-V格式的Entry数组,Key存的是ThreadLocal对象本身,value及需要存储的值。当我们要操作ThreadLocal变量时,会先获取当前的线程,根据当前线程拿到对应的ThreadLocalMap对象,进而操作ThreadLocalMap内部数组里存储的数据。

转载自享学课堂,原文地址:https://mp.weixin.qq.com/s/0aOGr-3W_EqUpvg1CHHy6g

你可能感兴趣的文章
计组期末复习题获取
查看>>
c++(STL中一些常用操作)
查看>>
php(环境搭建)
查看>>
php(变量)
查看>>
php(单双引号的区别)
查看>>
php(数据类型)
查看>>
php(运算符及流程控制)
查看>>
php(自定义函数与系统自带函数)
查看>>
php(从一个文件中调用另一个文件的变量)
查看>>
php(数组及相关操作)
查看>>
php(about error and time)
查看>>
利用php对数据库进行操作
查看>>
二叉树及其(前中后)序遍历
查看>>
2020.8.29 ssdh
查看>>
PyCharm使用技巧及常用快捷键
查看>>
ubuntu内存爆满卡住,一顿操作任务栏菜单栏消失再解决办法记录
查看>>
ubuntu下pycharm无法输入中文解决办法(记录)
查看>>
torch.cuda.is_available()返回False的解决办法
查看>>
BITVehicle_Dataset数据集转换
查看>>
将视频转存成图片小代码
查看>>