原创/朱季谦

登录模块里,当用户完成一次登录会话后,往往需要将其登录成功的信息进行缓存不同登录会话属于不同的会话线程,彼此需要互不影响。这就意味着,登录成功的信息,只属于该次会话线程本地变量,这时,就可以基于ThreadLocal缓存属于该会话线程的用户信息,类似线程的私有本地变量

可以这样实现,先定义一个存储用户信息的POJO类——

@Data
public class User {
    private String username;
    private String role;
    ......
}

一个专门缓存会话用户信息的类SessionCsche,在该类定义一个static修饰的ThreadLocal变量,再定义一个将User信息缓存到ThreadLocal的方法,以及从ThreadLocal取出方法——

public class SessionCsche {
    //定义一个static修饰的ThreadLocal变量
    private static ThreadLocal<User> threadCache = new ThreadLocal<>();
​
    //缓存用户信息
    public static void put(String username, String role) {
        User user = new User();
        user.setUsername(username);
        user.setRole(role);
        threadCache.set(user);
    }
​
  //读取缓存在该会话线程里的用户信息
    public static String getUsername() {
        User user = threadCache.get();
        if (user == null) {
            return null;
        }
        return user.getUsername();
    }
}

以上,就是基于ThreadLocal实现一个线程隔离缓存变量场景。除此之外,在DataSource数据源动态切换场景下,也可以基于ThreadLocal实现不同的线程获取不同的DataSource数据源,进而发起一次数据库访问会话。

以上主要是常见应用场景,在大概了解一下ThreadLocal的应用后,就可以阅读一下其内部源码实现类。

先来看threadCache.set(user)内部实现——

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

该源码首先通过 Thread t = Thread.currentThread()获取当前线程对象,再通过getMap(t)取出当前线程的ThreadLocalMap对象,如果获取到的map为空,就直接new ThreadLocal<&gt;()创建对象threadCache当作key,以需要缓冲数据当作value,以keyvalue格式缓存至线程的ThreadLocalMap对象里。若当前线程的map为空,则表示当前线程还没有创建ThreadLocalMap对象,因此就需要通过createMap(t, value)完成ThreadLocalMap对象创建后,再做缓存

简单看一下createMap(t, value)内部逻辑,可以看到里面通过new ThreadLocalMap(this, firstValue)创建了一个ThreadLocalMap对象,然后将ThreadLocal对象本身和需要缓存数据,当作参数传给该ThreadLocalMap构造方法——

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

进入new ThreadLocalMap(this, firstValue)内部,主要逻辑就是创建一个存放hash值到table数组长度INITIAL_CAPACITY默认是16,这与HashMap默认长度一致都是16,然后通过firstKey.threadLocalHashCode &amp; (INITIAL_CAPACITY – 1)对传进来的ThreadLocal对象当作key去做一次哈希计算计算其在数组中的索引位置然后在该位置创建新的Entry对象,用于存储键值对,这里的键是ThreadLocal对象,值是需要缓存的User用户数据

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode &amp; (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}

进入到Entry类,看一下其内部结构——

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;
​
    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

可见,这里的关键地方,正是这一个当前线程里的ThreadLocalMap对象。

每一个线程都有只属于本线程的ThreadLocalMap对象,类似线程的本地变量,当前线程只能读取自己的ThreadLocalMap对象缓存信息,使用到了一种用空间时间方案

接着,看一下ThreadLocal读取缓存的原理——

  User user = threadCache.get();

进入到get()内部,首先通过 Thread t = Thread.currentThread()获取当前线程对象,接着去取出当前Thread对象的ThreadLocalMap,若前面已经有数据缓存到ThreadLocalMap了,说明map就是有值的。这时候,通过ThreadLocalMap.Entry e = map.getEntry(this),以当前ThreadLocal对象当作key内部key.threadLocalHashCode &amp; (table.length – 1)方式计算出该key的在ThreadLocalMap内部数组哈希索引位置,进而取出该缓存数据赋值给e,如果e != null说明取出的缓存数据有效,就可以强制转换相应的类型对象,进而返回

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

原文地址:https://blog.csdn.net/weixin_40706420/article/details/134774586

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_36912.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注