Java中的ThreadLocal
Java中的ThreadLocal
变量值的共享可以使用public static变量的形式,所有线程可以共享一个变量。如果我们想实现一个线程都有自己的共享变量该如何解决呢,这里就要用到Java中的ThreadLocal类。
类ThreadLocal主要解决的就是每个线程绑定自己的值,可以将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的私有数据
我们先看下列代码
public static void main(String[] args) throws InterruptedException { ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){ @Override protected Integer initialValue() { return 0; } }; threadLocal.set(100); System.out.println("主线程中的值:"+threadLocal.get()); Thread t = new Thread(){ @Override public void run() { threadLocal.set(200); System.out.println("线程t中的值:"+threadLocal.get()); } }; t.start(); }
代码定义了一个ThreadLocal对象,然后在主线程中,给ThreadLocal对象放入100,而线程t则放入200,并打印。结果如下
可见,每个线程都取到了自己存入的那个值。那么它到底是怎么实现的呢,让我们来分析分析吧
首先先介绍一下ThreadLocal的一些主要方法
1、initalValue方法
protected T initialValue() { return null; }
initalValue方法,表示默认值,可见是null,但为了安全起见,我们在初始化ThreadLocal对象时采用匿名内部类来重写这个方法,以防止空指针异常。也就是这样:
ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){ @Override protected Integer initialValue() { return 0; } };
2、get方法
public T get() { //获取当前线程对象 Thread t = Thread.currentThread(); //取出线程类中的ThreadLocalMap ThreadLocal.ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocal.ThreadLocalMap.Entry e = map.getEntry(this); //如果Map不为空并且该线程确实存入了变量,则返回存入的变量 if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } //Map为空,返回默认值 return setInitialValue(); }
这里要说到,每个线程中都维护了一个ThreadLocalMap对象,这个Map就存储着你所存入的值,下面会说到。
3、set方法
public void set(T value) { //获取当前线程对象 Thread t = Thread.currentThread(); //根据线程获取线程中的ThreadLocalMap ThreadLocal.ThreadLocalMap map = getMap(t); //如果map不为空则直接存入值 if (map != null) map.set(this, value); //如果map为空,则创建Map并存入值 else createMap(t, value); }
4、remove方法
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
remove是移除所存入的值
5、getMap方法
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
根据线程去获得线程中的ThreadLocalMap对象
我们可以打开Thread类源码
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
这就是Thread类中的ThreadLocalMap对象,默认为空。只有你在使用ThreadLocal为线程保存私有属性时才会创建。
6、createMap方法
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
将线程中的ThreadLocalMap对象实例化并写入存入值
接下来就要看存储数据的ThreadLocalMap
static class ThreadLocalMap { //Map节点,注意key是ThreadLocal而不是Thread static class Entry extends WeakReference<ThreadLocal<?>> { Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } //构造方法 ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new ThreadLocal.ThreadLocalMap.Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); } }
Map很简单,只不过要注意的是Map的键是ThreadLocal而不是Thread。
总结
1、ThreadLocal是为了解决每个线程绑定自己的值
2、ThreadLocal默认值为null,所以有时候为了安全,初始化时使用匿名内部类来重写initalValue方法
3、每个线程对象内部都维护了一个ThreadLocalMao对象
4、ThreadLocalMap的键是ThreadLocal而不是Thread
5、ThreadlLocal可能会发生内存泄漏,所以在使用完ThreadLocal之后调用remove方法清除数据。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
编译型语言、解释型语言、静态类型语言、动态类型语言概念与区别
今天看到篇对于静态、动态、强类型、弱类型总结的很到位的一篇博文,在此Mark下。 编译型语言和解释型语言 1、编译型语言 需通过编译器(compiler)将源代码编译成机器码,之后才能执行的语言。一般需经过编译(compile)、链接(linker)这两个步骤。编译是把源代码编译成机器码,链接是把各个模块的机器码和依赖库串连起来生成可执行文件。 优点:编译器一般会有预编译的过程对代码进行优化。因为编译只做一次,运行时不需要编译,所以编译型语言的程序执行效率高。可以脱离语言环境独立运行。 缺点:编译之后如果需要修改就需要整个模块重新编译。编译的时候根据对应的运行环境生成机器码,不同的操作系统之间移植就会有问题,需要根据运行的操作系统环境编译不同的可执行文件。 代表语言:C、C++、Pascal、Object-C以及最近很火的苹果新语言swift 2、解释型语言 解释性语言的程序不需要编译,相比编译型语言省了道工序,解释性语言在运行程序的时候才逐行翻译。 优点:有良好的平台兼容性,在任何环境中都可以运行,前提是安装了解释器(虚拟机)。灵活,修改代码的时候直接修改就可以,可以快速部署,不用停...
- 下一篇
php中Session使用方法详解
php中Session使用方法详解 Session的声明与使用 Session的设置不同于Cookie,必须先启动,在PHP中必须调用session_start()。session_start()函数的语法格式如下: Bool session_start(void) //创建Session,开始一个会话,进行Session初始化 注意:session_start()函数之前不能有任何输出 当第一次访问网站时,Seesion_start()函数就会创建一个唯一的Session ID,并自动通过HTTP的响应头,将这个Session ID保存到客户端Cookie中。同时,也在服务器端创建一个以Session ID命名的文件,用于保存这个用户的会话信息。当同一个用户再次访问这个网站时,也会自动通过HTTP的请求头将Cookie中保存的Seesion ID再携带过来,这时Session_start()函数就不会再去分配一个新的Session ID,而是在服务器的硬盘中去寻找和这个Session ID同名的Session文件,将这之前为这个用户保存的会话信息读出,在当前脚本中应用,达到跟踪这个用...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Red5直播服务器,属于Java语言的直播服务器
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS关闭SELinux安全模块
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7