源码分析-ThreadLocal

ThreadLocal 始于JDK1.2,提供了一个操作线程对象变量(ThreadLocal.ThreadLocalMap)的方法,进而达到线程安全和数据与线程绑定的目的。

 

使用示例:

import org.junit.Test;
import java.util.concurrent.TimeUnit;

/**
 * ThreadLocal类使用测试
 */
public class ThreadLocalTest {

    public ThreadLocal<Integer> count = ThreadLocal.withInitial(() -> Integer.valueOf(0));

    @Test
    public void test() throws InterruptedException {
        ThreadLocalTest test = new ThreadLocalTest();
        new Thread(()->{
            while (true){
                System.out.println(Thread.currentThread().getName()+"---"+test.nextCount());
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(()->{
            while (true){
                System.out.println(Thread.currentThread().getName()+"---"+test.nextCount());
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(()->{
            while (true){
                System.out.println(Thread.currentThread().getName()+"---"+test.nextCount());
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(()->{
            while (true){
                System.out.println(Thread.currentThread().getName()+"---"+test.nextCount());
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        TimeUnit.DAYS.sleep(1);
    }

    private Integer nextCount() {
        Integer next = count.get();
        next++;
        count.set(next);
        return next;
    }
}

执行结果:

结果分析:

每个线程都有自己独有的count变量,互相不影响,无论更新还是查看,不存在线程安全问题。

 

源码分析:

主要是通过两个方法实现: count.get()   和   count.set(T t);

count.get()源码如下:

/**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        //获取到当前线程
        Thread t = Thread.currentThread();
        //用当前线程获取到一个ThreadLocalMap实例
        ThreadLocalMap map = getMap(t);
        //如果当前线程可以获取到ThreadLocalMap实例
        if (map != null) {
            //通过当前ThreadLocal对象,从刚才Thread中获取的ThreadLocalMap中获取一个Entry
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                //最终数据存储在Entry的value字段里
                T result = (T)e.value;
                return result;
            }
        }
        //如果当前线程获取不到一个ThreadLocalMap对象,则为当前线程初始化一个
        return setInitialValue();
    }

分析:首先count是ThreadLocal类的实例,所有调用的是ThreadLocal里面的get()方法。具体的分析在源码注释里面

问题1:如何通过当前线程获取到ThreadLocalMap对象?

源码如下:

/**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }



/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

分析:在java.lang.Thread中有一个成员变量,引用了ThreadLocal.ThreadLocalMap,所以相当于获取当前线程的成员变量的值

 

问题2:当前的变量值到底是怎么存储的?

一张图解决:

值存储在Thread类中的一个成员变量中(ThreadLocal.ThreadLocalMap)。这个成员变量是一个k,v结构的类似map的类实例。

key是ThreadLocal对象,也就是实例代码中的count,Map中是用private Entry[] table;存储数据的。也就是一个线程的map实例中可以存储多个k,v。

value是当前的业务set的对象,也就是实例代码中count.set(T t)的变量。

 

注意:

1.ThreadLocal是一个弱引用,在内存不足时,JVM会考虑回收掉,所以有可能在内存不足时线程绑定的变量有可能会被回收,也就是线程成员变量ThreadLocal.ThreadLocalMap这个map的k指向了一个被回收了的地址,即k是null了。导致缓存在线程本地的变量丢失。

2. ThreadLocal尽量不要和线程池混合使用,因为ThreadLocal是将变量保存到线程变量了,如果和线程池混合使用,那么这个线程不会被销毁,会被回收到线程池中分配给下一个任务,所以下一个任务拿到的新线程有可能有之前线程缓存的线程变量。

 

看完三件事❤️

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:

  1. 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。

  2. 关注公众号 『逆行的碎石机』,不定期分享原创知识。

  3. 同时可以期待后续文章ing🚀

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值