关于ThreadLocal的思考

一.关于ThreadLocal我们应该知道的

我们知道ThreadLocal被称作线程本地变量,它是一个以ThreadLocal对象为键,任意对象为值的存储结构(其实就是一个Map),这个结构被附带在线程上,也可以说是一个线程根据ThreadLocal对象查询绑定到这个线程上的一个值。

that accesses one (via its {@code get} or {@code set} method) has its own, independently initialized copy of the variable.  {@code ThreadLocal} instances are typically private static fields in classes that wish to associate state with a thread (e.g.,a user ID or Transaction ID)
1

 上面这句英语是官网给的解释,翻译过来如下:ThreadLocal类用来提供线程内部的局部变量,这些变量在多线程访问的情况下(通过get()或set()方法访问)能保证各个线程里的变量相对独立于其他线程里的变量,ThreadLocal通常都是private static类型。

总结:ThreadLocal是线程为自己存储某一个变量(或者说对象)而提供的一种手段,提供了保持对象的方法和避免参数传递的复杂性

 

二.ThreadLocal源码解析

1.ThreadLocal的源码较多,以下是ThreadLocal类里主要的一些变量和静态内部类:

public class ThreadLocal<T> {
    
    private final int threadLocalHashCode = nextHashCode();

    //AtomicInteger类可以线程安全的进行加减操作
    private static AtomicInteger nextHashCode =
        new AtomicInteger();

    
    private static final int HASH_INCREMENT = 0x61c88647;

    //getAndAdd方法就是AtomicInteger类中的方法,可以线程安全的将原来的值加上一个给定的增量
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);

    //ThreadLocalMap是ThreadLocal的静态内部类
    static class ThreadLocalMap {

        //Entry又是ThreadLocalMap的静态内部类
        static class Entry extends WeakReference<ThreadLocal<?>> {
            
            Object value;
            
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
    
    }

 事实上ThreadLocal里面的大部分东西都是通过静态内部类实现的,它的set方法,get方法,remove方法等的实现都是在静态内部类中实现,我们将在下面的源码中看见

2.我们可以用ThreadLocal中的set方法保存一个值,也就是设置当前线程的本地变量的值。

public void set(T value) {
        //得到当前线程
        Thread t = Thread.currentThread();
        //调用getMap方法,为了方便,将getMap方法放在下面
        ThreadLocalMap map = getMap(t);
        //判断是否为空
        if (map != null)
            //这里需要注意key是this,而不是很多人说的线程的名字或者标识
            map.set(this, value);//调用静态内部类的set方法
        else
            createMap(t, value); //如果为空,就先创建一个Map
    }

-----------------------------------------------------
参数t表示当前线程,所以返回的就是当前线程的threadLocals
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

------------------------------------------------------

//一个ThreadLocalMap类型的threadLocals变量,初始值为null
ThreadLocal.ThreadLocalMap threadLocals = null;

3.当我们set一个值以后,可以通过get方法取到这个值。

public T get() {
        //得到当前线程
        Thread t = Thread.currentThread();
        //同样是调用getMap方法,得到当前线程的Map
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            //调用getEntry方法,getEntry方法在下边
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                //该批注的作用是给编译器一条指令,告诉它对被批注代码里的某些警告保持静默
                @SuppressWarnings("unchecked")
                //拿到这个key对应的value
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

-----------------------------------------------------------------

private Entry getEntry(ThreadLocal<?> key) {
            //计算key在数组中的位置
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            //判断取到的key不为空,并且数组中返回的key与参数key是否相等
            if (e != null && e.get() == key)  //get方法是静态内部类中的方法
                return e;
            else
                return getEntryAfterMiss(key, i, e); // 这个方法最后返回的会是null
        }

 4.如果我们想移除这个值,可以用remove方法,remove方法源码如下:

这是ThreadLocal里面的remove方法
public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
            调用下面的remove方法
             m.remove(this);
     }

----------------------------------------------------------------

//这是静态内部类Entry里的remove,remove方法的主要逻辑也在这里面
  private void remove(ThreadLocal<?> key) {
            Entry[] tab = table;
            int len = tab.length;
            //计算key在数组中的位置,在前面的get方法也有这一步
            int i = key.threadLocalHashCode & (len-1);
            //遍历数组找到这个key
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                if (e.get() == key) {
                    //清除
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

三.ThreadLocal的应用

ThreadLocal的应用非常广泛,在spring和Hibernate框架作用非常大,还有Sesssion管理之类的。比如:每用户登陆网站,用户服务器都需要为其创建一个线程,我们可以将用户的信息存储在线程的ThreadLocal中,在页面跳转时,新页面可能需要一些用户信息,我们可以在ThreadLocal中取得。

四.总结

通过上面的分析,我们可以知道ThreadLocal通过静态内部类ThreadLocalMap实现,ThreadLocalMap是一个Map,当调用set方法时,这个map的key值,也就是this其实就是一个ThreadLocal类型的变量(线程自己的变量)。因此这个变量对应的value也只能由这个线程自己可以取到,因此我们可以说每一个线程独自享有一个变量,与其他线程无关。

因此ThreadLocal虽然是用来解决多线程问题的,但是它只局限于自己的这个线程,ThreadLocal变量的活动范围为某线程,对该变量的所有操作均由该线程完成。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadLocal是Java中一个非常重要的线程封闭技术。它可以让每个线程都拥有自己的变量副本,避免了线程间的竞争和数据泄露问题。在本文中,我们将详细介绍ThreadLocal的定义、用法及其优点。 1. ThreadLocal的定义 ThreadLocal是Java中一个用来实现线程封闭技术的类。它提供了一个本地线程变量,可以在多线程环境下使每个线程都拥有自己的变量副本。每个线程都可以独立地改变自己的副本,而不会影响到其他线程的副本。ThreadLocal的实现是基于ThreadLocalMap的,每个ThreadLocal对象都对应一个ThreadLocalMap,其中存储了线程本地变量的值。 2. ThreadLocal的用法 使用ThreadLocal非常简单,只需要创建一个ThreadLocal对象,然后调用其get()和set()方法即可。get()方法用来获取当前线程的变量副本,如果当前线程还没有变量副本,则会创建一个新的副本并返回。set()方法用来设置当前线程的变量副本,如果当前线程已经有了变量副本,则会覆盖原来的副本。 下面是一个简单的例子,演示了如何使用ThreadLocal来实现线程封闭: ```java public class ThreadLocalTest { private static ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) throws InterruptedException { new Thread(() -> { threadLocal.set("Thread A"); System.out.println("Thread A: " + threadLocal.get()); }).start(); new Thread(() -> { threadLocal.set("Thread B"); System.out.println("Thread B: " + threadLocal.get()); }).start(); Thread.sleep(1000); System.out.println("Main: " + threadLocal.get()); } } ``` 运行结果如下: ``` Thread A: Thread A Thread B: Thread B Main: null ``` 从输出结果可以看出,每个线程都拥有自己的变量副本,互不影响。而在主线程中,由于没有设置过变量副本,所以返回null。 3. ThreadLocal的优点 ThreadLocal的优点主要体现在以下几个方面: (1)线程安全:ThreadLocal可以避免线程间的竞争和数据泄露问题,每个线程都可以独立地修改自己的变量副本,不会影响其他线程。 (2)高效性:ThreadLocal使用起来非常简单,而且性能也非常高,比如在Web开发中,可以将用户信息存储在ThreadLocal中,从而避免在每个方法中都去查询数据库。 (3)易用性:ThreadLocal的使用非常灵活,可以根据实际需要自由地定义数据类型和访问方式。 总的来说,ThreadLocal是Java中一个非常重要的线程封闭技术,可以帮助开发人员避免线程间的竞争和数据泄露问题,提高程序的安全性和性能。在实际开发中,我们应该充分利用ThreadLocal的优点,合理地运用它来解决各种线程安全问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值