ThreadLocal的学习

一 用法
ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
1、ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。
2、ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值。
3、ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。
4、ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值。

二 写法

/**
 * 用户管理工具
 */
public class UserManager {
    /**
     * 用于存放当前的用户信息
     */
    private static final ThreadLocal<AdminRoleDto> USERINFO_CACHE = new ThreadLocal<AdminRoleDto>();


    /**
     * 清空用户信息
     */
    public static void removeLoginUser() {
        USERINFO_CACHE.remove();
    }

    /**
     * 设置用户对象信息
     * @param adminRoleDto
     */
    public static void setLoginUser(AdminRoleDto adminRoleDto) {
        USERINFO_CACHE.set(adminRoleDto);
    }
    
    /**
     * 获取当前用户信息
     * @return
     */
    public static AdminRoleDto getLoginUser() {
       return USERINFO_CACHE.get();
    }

    /**
     *判断当前用户是否登录
     * @return
     */
    public static Boolean isLogin(){
        if(USERINFO_CACHE.get() == null) {
            return false;
        }else {
            return true;
        }
    }
}

3 问题

  1.如何实现线程安全
Thread 类中有ThreadLocal.ThreadLocalMap的变量,ThreadLocal.ThreadLocalMap.Entry变量类似于Map的形式存放需要存储的数据.
也就是说存放的数据是依赖于线程的,即每个线程都有自己的变量来存储数据,数据的读写都是在当前线程中的,别的线程是无法访问操作的,也就没有了线程安全的问题.
  2.ThreadLocal如何避免内存泄漏
ThreadLocal.ThreadLocalMap.Entry 的内部结构是

static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;

Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}

如果Entry 的key(ThreadLocal) 回收的话,在调用get/set/remove的时候就会自动调用相关的方法去释放value的值,实现回收.

或者 线程结束时释放TreadLocalMap实现回收数据

个人认为我们一般用Thread基本都是线程池,不会关闭线程,而我们使用TreadLocal的时候基本都是静态的(因为TreadLocal 的get方法要以 当前的对象作为key 需要单例),这样的话Tread不会关闭,TreadLocal不会置为null,所以很容易造成内存泄漏.平常的话还是要手动调用remove 方法来防止内存泄漏

注意点

1.每个线程都有一个ThreadLocalMap 类型的 threadLocals 属性。
2.ThreadLocalMap 类相当于一个Map,key 是 ThreadLocal 本身,value 就是我们的值。
3.当我们通过 threadLocal.set(new Integer(123)); ,我们就会在这个线程中的 threadLocals 属性中放入一个键值对,key 是 这个 threadLocal.set(new Integer(123))的threadlocal,value 就是值new Integer(123)。
4.当我们通过 threadlocal.get() 方法的时候,首先会根据这个线程得到这个线程的 threadLocals 属性,然后由于这个属性放的是键值对,我们就可以根据键 threadlocal 拿到值。 注意,这时候这个键 threadlocal 和 我们 set 方法的时候的那个键 threadlocal 是一样的,所以我们能够拿到相同的值。
5.ThreadLocalMap 的get/set/remove方法跟HashMap的内部实现都基本一样,通过 "key.threadLocalHashCode & (table.length - 1)" 运算式计算得到我们想要找的索引位置,如果该索引位置的键值对不是我们要找的,则通过nextIndex方法计算下一个索引位置,直到找到目标键值对或者为空。
6.hash冲突:在HashMap中相同索引位置的元素以链表形式保存在同一个索引位置;而在ThreadLocalMap中,没有使用链表的数据结构,而是将(当前的索引位置+1)对length取模的结果作为相同索引元素的位置:源码中的nextIndex方法,可以表达成如下公式:如果i为当前索引位置,则下一个索引位置 = (i + 1 < len) ? i + 1 : 0。
7.get/set/remove 都有遍历删除 key为null的数据的操作

猜你喜欢

转载自www.cnblogs.com/codeLei/p/9915682.html