文章目录
1、什么是ThreadLocal?
- 根据字面意思:thread(线程)、local(本地),那么ThreadLocal就是线程本地(线程变量),即:ThreadLocal中填充的变量属于当前线程,既然属于当前线程的,也就解决了线程安全问题。
- ThreadLocal变量,线程局部变量,同一个ThreadLocal包含的对象所在不同的Thread中有不同的副本。
- ThreadLocal变量提供线程本地实例,与普通变量的区别:
- 每个使用该变量的线程都会初始化一个完全独立的实例副本。
- ThreadLocal变量通常被private static修饰的。
- 当一个线程结束的时候,所使用的所有的ThreadLocal实力副本都可被回收。
2、ThreadLocal原理
2.1、set
- 源码中,首先是获取当前线程对象,然后根据getMap()拿到这个线程对象的ThreadLocalMap(简单的先理解为HashMap),判断map是否存在,存在就去设置值,不存在就去createMap。
- 一个Thread有一个ThreadLocalMap,而ThreadLocalMap可以存放多个Entry { “key”:ThreadLocal :“value”: object }
public class ThreadLocalTest {
static ThreadLocal<Integer> threadLocal1 = new ThreadLocal<>();
static ThreadLocal<Integer> threadLocal2 = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
try{
System.out.println(threadLocal1.get());
threadLocal1.set(1);
System.out.println(threadLocal1.get());
System.out.println(threadLocal2.get());
threadLocal2.set(2);
System.out.println(threadLocal2.get());
}finally {
threadLocal1.remove();
threadLocal2.remove();
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
try{
System.out.println(threadLocal1.get());
threadLocal1.set(3);
System.out.println(threadLocal1.get());
System.out.println(threadLocal2.get());
threadLocal2.set(4);
System.out.println(threadLocal2.get());
}finally {
threadLocal1.remove();
threadLocal2.remove();
}
}
});
threadA.start();
Thread.sleep(200);
System.out.println("============");
threadB.start();
}
}
4. 每次设置的key值当前ThreadLocal实例对象,value就是真正输入的Object值。
2.2、get
- 通过源码:先得到当前线程对象,再通过这个对象得到ThreadLocalMap,map不为空,那么将当前ThreadLocal对象作为key取出得到内部实际数据。
2.3、remove
- 通过源码,同样,获取当前线程的的ThreadLocalMap,不为空,那么将当前ThreadLocal对象作为参数传入,进行删除。
3、内存泄露
- 由源码可见,ThreadLocalMap(本身就是一个静态内部类)在它的内部还有一个静态内部类(Entry)。
- ThreadLocalMap内部由一系列Entry构成的,每一个Entry都是
WeakReference<ThreadLocal>
。 - 参数k就是Map的key,v就是Map的value。k也就是ThreadLocal对象实例,作为弱引用使用(super(k)就是调用了WeakReference构造函数)。
- 弱引用的特点:如果这个对象只存在弱引用,那么是存活不到下次垃圾回收的时候。
- 那么ThreadLocal没有被外部强引用的情况下,在垃圾回收的时候会被清理,key是一个弱引用,自然会被清理掉,但是value是一个强引用,不会被清理,这样就会出现key为null的value。
- ThreadLocal内存泄漏的真正原因:ThreadLocal的声明周期与Thread是一致的,没有手动删除对应的value就会导致内存泄露。
4、Thread、ThreadLocal、ThreadLocalMap的关系
- 先看源码:
可以看到,Thread中有一个属性叫threadLocals,类型是ThreadLocal.ThreadLocalMap的。那么也就是:ThreadLocalMap其实就是Thread的一个属性值,ThreadLocal就是维护ThreadLocalMap。
Thread可以拥有多个ThreadLocal维护自己线程独享的共享变量。
5、使用场景
- 存储用户session信息。
- 数据库连接,处理数据库事务。
- 数据跨层传递(controller、service、dao)。
package com.kong.threadlocal;
public class ThreadLocalDemo05 {
public static void main(String[] args) {
User user = new User("jack");
new Service1().service1(user);
}
}
class Service1 {
public void service1(User user){
//给ThreadLocal赋值,后续的服务直接通过ThreadLocal获取就行了。
UserContextHolder.holder.set(user);
new Service2().service2();
}
}
class Service2 {
public void service2(){
User user = UserContextHolder.holder.get();
System.out.println("service2拿到的用户:"+user.name);
new Service3().service3();
}
}
class Service3 {
public void service3(){
User user = UserContextHolder.holder.get();
System.out.println("service3拿到的用户:"+user.name);
//在整个流程执行完毕后,一定要执行remove
UserContextHolder.holder.remove();
}
}
class UserContextHolder {
//创建ThreadLocal保存User对象
public static ThreadLocal<User> holder = new ThreadLocal<>();
}
class User {
String name;
public User(String name){
this.name = name;
}
}
执行的结果:
service2拿到的用户:jack
service3拿到的用户:jack
6、自己实现一个ThreadLocal
public class MyDemo1 {
private static ThreadLocal<String> tl = new ThreadLocal<>();
private String content;
private String getContent() {
return tl.get();
}
private void setContent(String content) {
tl.set(content);
}
public static void main(String[] args) {
MyDemo demo = new MyDemo();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
demo.setContent(Thread.currentThread().getName() + "的数据");
System.out.println("-----------------------");
System.out.println(Thread.currentThread().getName() + "--->" + demo.getContent());
}
});
thread.setName("线程" + i);
thread.start();
}
}
}