解决hash冲突:
在java线程中,每个线程都有一个ThreadLocalMap实例变量(如果不使用ThreadLocal,不会创建这个Map,一个线程第一次访问某个ThreadLocal变量时,才会创建)。该Map是使用线性探测的方式解决hash冲突的问题,如果没有找到空闲的slot,就不断往后尝试,直到找到一个空闲的位置,插入entry,这种方式在经常遇到hash冲突时,影响效率。
ThreadLocal为每个使用该变量的线程提供独立的变量副本(每一个线程都可以独立地改变自己的副本,不影响其它线程所对应的副本) 工作内存一样;
ThreadLocal的内部结构如下图所示
- 工具类,提供一系列的方法去操作其内部类 ThreadLocalMap ;
- 隔离Thread和ThreadLocalMap,防止程序员直接创建ThreadLocalMap(这两个类都处在java.lang包下);
如何使用ThreadLocal
在介绍ThreadLocal原理之前,首先简单介绍一下它的使用方法。
public class Main{
//线程共享的ThreadLocal对象,该对象用于存储Integer类型的值;
private ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
public void start() {
for (int i=0; i<10; i++) {
new Thread(new Runnable(){
@override
public void run(){
threadLocal.set(i);
threadLocal.get();
threadLocal.remove();
}
}).start();
}
}
}
public class Person {
String name = "zs";
}
public class ThreadLocal1 {
volatile static Person p = new Person();
public static void main(String[] args) {
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(p.name);
}).start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
p.name = "ls";
}).start();
}
}
public class ThreadLocal2 {
private static ThreadLocal<Person> tl = new ThreadLocal<>();
public static void main(String[] args) {
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(tl.get());
}).start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
tl.set(new Person());
}).start();
}
}
ThreadLocal
并不维护ThreadLocalMap
,
提供了操作该容器的方法,如get、set、remove等。而ThreadLocal
内部类ThreadLocalMap
才是存储数据的容器,并且该容器由Thread
维护。
ThreadLocalMap: Entry<ThreadLocal(对象),Object(对应的值)>
ThreadLocal 工具类 提供了操作threadLocals(ThreadLocalMap )的方法,如get,set,remove方法等。
Thread 的成员变量 ThreadLocalMap threadLocals
,:存放别线程中所有ThreadLocal对象及其对应的值;
项目中使用
在写代码insert和update的时候,往往需要存入当前操作者的id或者name,来记录这条数据是谁更新的。
我们可以在过滤器验证权限时,从redis中拿出session信息,存放ThreadLocal中。
doFilter后,再remove ThreadLocal。
代码参考,细节省略:
public class Sample {
private static final ThreadLocal<Map> ThreadLocal_Map = new ThreadLocal<Map>();
public static void setMap(Map map) {
ThreadLocal_Map.set(map);
}
// remove
public static void closeMap() {
Map map = ThreadLocal_Map.get();
if (map != null) {
map.clear();
ThreadLocal_Map.remove();
}
}
public static String getUserName() {
Map map = ThreadLocal_Map.get();
return map.getOrDefault("userName", "system").toString();
}
}