谈谈Java中的ThreadLocal
作者:dreamcatcher-cx 发布时间:[ 2016/12/14 14:17:32 ] 推荐标签:测试开发技术 Java
什么是ThreadLocal
ThreadLocal一般称为线程本地变量,它是一种特殊的线程绑定机制,将变量与线程绑定在一起,为每一个线程维护一个独立的变量副本。通过ThreadLocal可以将对象的可见范围限制在同一个线程内。
跳出误区
需要重点强调的的是,不要拿ThreadLocal和synchronized做类比,因为这种比较压根是无意义的!sysnchronized是一种互斥同步机制,是为了保证在多线程环境下对于共享资源的正确访问。而ThreadLocal从本质上讲,无非是提供了一个“线程级”的变量作用域,它是一种线程封闭(每个线程独享变量)技术,更直白点讲,ThreadLocal可以理解为将对象的作用范围限制在一个线程上下文中,使得变量的作用域为“线程级”。
没有ThreadLocal的时候,一个线程在其声明周期内,可能穿过多个层级,多个方法,如果有个对象需要在此线程周期内多次调用,且是跨层级的(线程内共享),通常的做法是通过参数进行传递;而ThreadLocal将变量绑定在线程上,在一个线程周期内,无论“你身处何地”,只需通过其提供的get方法可轻松获取到对象。极大地提高了对于“线程级变量”的访问便利性。
来看个简单的例子
假设我们要为每个线程关联一个的序号,在每个线程周期内,我们需要多次访问这个序号,这时我们可以使用ThreadLocal了.(当然下面这个例子没有完全体现出跨层级跨方法的调用,理解可以了)
package concurrent;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by chengxiao on 2016/12/12.
*/
public class ThreadLocalDemo {
public static void main(String []args){
for(int i=0;i<5;i++){
final Thread t = new Thread(){
@Override
public void run(){
System.out.println("当前线程:"+Thread.currentThread().getName()+",已分配ID:"+ThreadId.get());
}
};
t.start();
}
}
static class ThreadId{
//一个递增的序列,使用AtomicInger原子变量保证线程安全
private static final AtomicInteger nextId = new AtomicInteger(0);
//线程本地变量,为每个线程关联一个的序号
private static final ThreadLocal<Integer> threadId =
new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return nextId.getAndIncrement();//相当于nextId++,由于nextId++这种操作是个复合操作而非原子操作,会有线程安全问题(可能在初始化时获取到相同的ID,所以使用原子变量
}
};
//返回当前线程的的序列,如果第一次get,会先调用initialValue,后面看源码了解了
public static int get() {
return threadId.get();
}
}
}
执行结果,可以看到每个线程都分配到了一个的ID,同时在此线程范围内的"任何地点",我们都可以通过ThreadId.get()这种方式直接获取。
当前线程:Thread-4,已分配ID:1
当前线程:Thread-0,已分配ID:0
当前线程:Thread-2,已分配ID:3
当前线程:Thread-1,已分配ID:4
当前线程:Thread-3,已分配ID:2
看看源码
set操作,为线程绑定变量
public void set(T value) {
Thread t = Thread.currentThread();//1.首先获取当前线程对象
ThreadLocalMap map = getMap(t);//2.获取该线程对象的ThreadLocalMap
if (map != null)
map.set(this, value);//如果map不为空,执行set操作,以当前threadLocal对象为key,实际存储对象为value进行set操作
else
createMap(t, value);//如果map为空,则为该线程创建ThreadLocalMap
}
可以看到,ThreadLocal不过是个入口,真正的变量是绑定在线程上的。
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;//线程对象持有ThreadLocalMap的引用
}
下面给是Thread类中的定义,每个线程对象都拥有一个ThreadLocalMap对象
ThreadLocal.ThreadLocalMap threadLocals = null;
现在,我们能看出ThreadLocal的设计思想了:
1.ThreadLocal仅仅是个变量访问的入口;
2.每一个Thread对象都有一个ThreadLocalMap对象,这个ThreadLocalMap持有对象的引用;
3.ThreadLocalMap以当前的threadlocal对象为key,以真正的存储对象为value。get时通过threadlocal实例可以找到绑定在当前线程上的对象。
乍看上去,这种设计确实有些绕。我们完全可以在设计成Map<Thread,T>这种形式,一个线程对应一个存储对象。ThreadLocal这样设计的目的主要有两个:
一是可以保证当前线程结束时相关对象能尽快被回收;二是ThreadLocalMap中的元素会大大减少,我们都知道map过大更容易造成哈希冲突而导致性能变差。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11