博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ThreadLocal
阅读量:4921 次
发布时间:2019-06-11

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

简单介绍

ThreadLocal一般称为线程本地变量,它是一种特殊的线程绑定机制,将变量与线程绑定在一起,为每一个线程维护一个独立的变量副本。通过ThreadLocal可以将对象的可见范围限制在同一个线程内。

跳出误区

需要重点强调的的是,不要拿ThreadLocal和synchronized做类比,因为这种比较压根就是无意义的!sysnchronized是一种互斥同步机制,是为了保证在多线程环境下对于共享资源的正确访问。而ThreadLocal从本质上讲,无非是提供了一个“线程级”变量作用域,它是一种线程封闭(每个线程独享变量)技术,更直白点讲,ThreadLocal可以理解为将对象的作用范围限制在一个线程上下文中,使得变量的作用域为“线程级”。

没有ThreadLocal的时候,一个线程在其生命周期内,可能穿过多个层级,多个方法,如果有个对象需要在此线程周期内多次调用,且是跨层级的(线程内共享),通常的做法是通过参数进行传递;而ThreadLocal将变量绑定在线程上,在一个线程周期内,无论“你身处何地”,只需通过其提供的get方法就可轻松获取到对象。极大地提高了对于“线程级变量”的访问便利性。

举个栗子

在一个AccountService中有一段类似这样的代码:

Context ctx = new Context();ctx.setTrackerID(.....)

这个AccountService 调用了其他Java类,不知道经过了多少层调用以后,最终来到了一个叫做AccountUtil的地方,在这个类中需要使用Context中的trackerID来做点儿事情:

很明显,这个AccountUtil没有办法拿到Context对象, 怎么办,把Context对象一层层地传递下去,这样AccountUtil不就可以得到了吗?

 

可是这么做改动量太大,涉及到的每一层函数调用都得改动,有很多类都不属于自己的小组管理,还得和别人协调。有些类根本就没有源码,想改都改不了。

另一种方式是,可以把那个set/get TrackerID的方法改成静态(static)的,这样不管跨多少层调用都没有问题!

public class ContextHolder {    public static String getTrackerID(){        ......    }    public static void setTrackerID(String id){        ......    }}

但是这个做又存在一个线程问题: 多线程并发的时候出错,线程1调用了Context.setTrackerID(), 线程2 也调用了Context.setTrackerID(),数据互相覆盖,就会出错。

像这样中情况,需要在某处设置一个值,然后经过重重方法调用,到了另外一处把这个值取出来,又要线程安全,就可以把这个值就放到线程中,让线程携带着这个值到处跑,这样无论在任何地方都可以轻松获得了。每个线程都有一个私家领地,在Thread这个类中有个专门的数据结构,叫做threadLocals的变量,还是个Map类型 ,可以放入TrackerID,然后到任何地方都可以把这个TrackerID给取出来。但是在Thread类中没有对这个变量操作的方法,这个变量不是通过Thread访问的,对他的访问委托给了ThreadLocal。使用时,创建一个ThreadLocal类的实例:

ThreadLocal
threadLocalA= new ThreadLocal
();线程1: threadLocalA.set("1234");线程2: threadLocalA.set("5678");

像‘1234’, ‘5678’这些值都会放到自己所属的线程对象中。使用的时候用get()方法取出:

线程1: threadLocalA.get()  --> "1234"线程2: threadLocalA.get() --> "5678"

相当于把各自的数据放入到了各自Thread这个对象中去了,每个线程的值自然就区分开了。

threadLocals变量是一个定制的ThreadLocalMap类型,在这个类上的源码有说明:ThreadLocalMap is a customized hash map suitable only for maintaining thread local values. 这个定义的好处是,能存储多个ThreadLocal对象。假设你创建了另外一个threadLocalB:

ThreadLocal
threadLocalB = new ThreadLocal
();线程1: threadLocalB.set(30);线程2: threadLocalB.set(40);

那线程对象的Map就起到作用了:

现在那个Context可以改成,使用ThreadLocal:

 

public class ContextHolder {    private static final ThreadLocal
mThreadLocal = new ThreadLocal
(); public static void setTrackerID(String id) { mThreadLocal.set(id); } public static String getTrackerID() { return mThreadLocal.get(); } }

更多的时候,用的是ThreadLocal<XXXContext>,即定义一个Context对象,在线程上下文中都要用到的参数全都放入Context里,再用ContextHolder.get().setParamsA(a)去填值。

 

ThreadLocal这个名字起得有点让人误解, 很容易让人认为是“本地线程”, 其实是用来维护本线程的变量。 

ThreadLocal 并不仅仅是Java中的概念,其他语言例如Python,C#中也有,作用类似。

ThreadLocal在日常工作中用得不多,但是在框架(如Spring)中是个基础性的技术,在事务管理,AOP等领域都能找到。

 

 参考:

 

转载于:https://www.cnblogs.com/boomoom/p/8872692.html

你可能感兴趣的文章
GridView,Repeater增加自动序号列
查看>>
SMO算法精解
查看>>
第k小元素学习记录
查看>>
avi文件格式详解【转】
查看>>
django
查看>>
Java学习从入门到精通
查看>>
查找目录下的所有文件中是否含有某个字符串 linux
查看>>
2018年各大互联网前端面试题四(美团)
查看>>
一起学Python:字符串介绍
查看>>
学习笔记:树状数组
查看>>
洛谷P1772 [ZJOI2006]物流运输 题解
查看>>
CF519E A and B and Lecture Rooms
查看>>
python-redis之数据类型二
查看>>
Java类加载机制
查看>>
循环单链表实现
查看>>
Android设计模式实战---责任链模式
查看>>
剑指Offer_31_整数中1出现的次数(从1到n整数中1出现的次数)
查看>>
10月29日 迅雷会员vip账号分享 91freevip 晚间21:00更新
查看>>
【一题多解】Python 字符串逆序
查看>>
open ball、closed ball 与 open set、closed set(interior point,limit point)、dense set
查看>>