浅谈ThreadLocal

1、什么是ThreadLocal?

  1. 根据字面意思:thread(线程)、local(本地),那么ThreadLocal就是线程本地(线程变量),即:ThreadLocal中填充的变量属于当前线程,既然属于当前线程的,也就解决了线程安全问题。
  2. ThreadLocal变量,线程局部变量,同一个ThreadLocal包含的对象所在不同的Thread中有不同的副本。
  3. ThreadLocal变量提供线程本地实例,与普通变量的区别:
    1. 每个使用该变量的线程都会初始化一个完全独立的实例副本。
    2. ThreadLocal变量通常被private static修饰的。
    3. 当一个线程结束的时候,所使用的所有的ThreadLocal实力副本都可被回收。

2、ThreadLocal原理

2.1、set

在这里插入图片描述

  1. 源码中,首先是获取当前线程对象,然后根据getMap()拿到这个线程对象的ThreadLocalMap(简单的先理解为HashMap),判断map是否存在,存在就去设置值,不存在就去createMap。
  2. 一个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

在这里插入图片描述

  1. 通过源码:先得到当前线程对象,再通过这个对象得到ThreadLocalMap,map不为空,那么将当前ThreadLocal对象作为key取出得到内部实际数据。

2.3、remove

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 通过源码,同样,获取当前线程的的ThreadLocalMap,不为空,那么将当前ThreadLocal对象作为参数传入,进行删除。

3、内存泄露

在这里插入图片描述

  1. 由源码可见,ThreadLocalMap(本身就是一个静态内部类)在它的内部还有一个静态内部类(Entry)。
  2. ThreadLocalMap内部由一系列Entry构成的,每一个Entry都是WeakReference<ThreadLocal>
  3. 参数k就是Map的key,v就是Map的value。k也就是ThreadLocal对象实例,作为弱引用使用(super(k)就是调用了WeakReference构造函数)。
  4. 弱引用的特点:如果这个对象只存在弱引用,那么是存活不到下次垃圾回收的时候。
  5. 那么ThreadLocal没有被外部强引用的情况下,在垃圾回收的时候会被清理,key是一个弱引用,自然会被清理掉,但是value是一个强引用,不会被清理,这样就会出现key为null的value。
  6. ThreadLocal内存泄漏的真正原因:ThreadLocal的声明周期与Thread是一致的,没有手动删除对应的value就会导致内存泄露。

4、Thread、ThreadLocal、ThreadLocalMap的关系

  1. 先看源码:
    在这里插入图片描述
    在这里插入图片描述
    可以看到,Thread中有一个属性叫threadLocals,类型是ThreadLocal.ThreadLocalMap的。那么也就是:ThreadLocalMap其实就是Thread的一个属性值,ThreadLocal就是维护ThreadLocalMap。
    在这里插入图片描述

Thread可以拥有多个ThreadLocal维护自己线程独享的共享变量。

5、使用场景

  1. 存储用户session信息。
  2. 数据库连接,处理数据库事务。
  3. 数据跨层传递(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();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值