ThreadLocal

目录

1. 封装一个Util

2. 更多ThreadLocal

2.1 原理(很有用)

2.2 什么是ThreadLocal

2.3 setInitialValue()

2.4 其他使用场景

2.4.1 数据库连接

2.4.2 Session管理

2.5 其他不错的博客讲解ThreadLocal


1. 封装一个Util

对于ThreadLocal,

private static final ThreadLocal<HashMap> threadLocal = ThreadLocal.withInitial(() -> new HashMap());

思想首先是初始化一个ThreadLocal,然后又用来存放数据。

public class ThreadLocalUtil {

    //初始化threadLocal,存入一个hashMap用于存取数据
    private static final ThreadLocal<HashMap> threadLocal = ThreadLocal.withInitial(() -> new HashMap());

    public static void add(HashMap map) {
        threadLocal.set(map);
    }

    private static HashMap getMap() {
        return threadLocal.get();
    }

    public static void remove() {
        threadLocal.remove();
    }

    public static void setRequestInfo(String dataSource, String url) {
        HttpServletResponse response = (HttpServletResponse) getMap().get("response");
        response.setHeader("dataSource", dataSource);
        getMap().put("dataSource", dataSource);
        getMap().put("url", url);
    }

}  

这是个基本的思想,可以在自己往里面填充要放入ThreadLocal的东西

比如我Map中有放入Url

public static String getUrl() {
    return (String) getMap().get("url");
}

那么在Util 中加入这个方法就可以获取到这个url

这个的生命周期是相对 请求进来,spring来控制管理线程,

然后线程结束,显示的进行remove掉!

2. 更多ThreadLocal

ThreadLocal 用于保存某个线程的共享变量:对于同一个static ThreadLocal  不同线程只能从中get set remove自己的变量

而不会影响到其他线程的变量。

1、ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。

2、ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值。

3、ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。

4、ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用调用get方法 获取不到Map,返回此方法值。用时进行 重写的,它是一个延迟加载的方法

2.1 原理(很有用)

线程共享变量缓存如下:

Thread.ThreadLocalMap<ThreadLocalObject>;

1、Thread: 当前线程,可以通过Thread.currentThread()获取。

2、ThreadLocal:我们的static ThreadLocal变量。

3、Object: 当前线程共享变量。

我们调用ThreadLocal.get方法时,实际上是从当前线程中获取ThreadLocalMap<ThreadLocalObject>,然后根据当前ThreadLocal获取当前线程共享变量Object。

ThreadLocal.set,ThreadLocal.remove实际上是同样的道理。

 

这种存储结构的好处:

1、线程死去的时候,线程共享变量ThreadLocalMap则销毁。

2、ThreadLocalMap<ThreadLocal,Object>键值对数量为ThreadLocal的数量,一般来说ThreadLocal数量很少,相比在ThreadLocal中用Map<Thread, Object>键值对存储线程共享变量(Thread数量一般来说比ThreadLocal数量多),性能提高很多。

关于ThreadLocalMap<ThreadLocalObject>弱引用问题:

当线程没有结束,但是ThreadLocal已经被回收,则可能导致线程中存在ThreadLocalMap<nullObject>的键值对,造成内存泄露。(ThreadLocal被回收,ThreadLocal关联的线程共享变量还存在)。

虽然ThreadLocal的get,set方法可以清除ThreadLocalMap中key为null的value,但是get,set方法在内存泄露后并不会必然调用,所以为了防止此类情况的出现,我们有两种手段。

1、使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;

2、JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。

关注点:弱引用 需要定义为priovate static ;然后需要线程使用完,显示调用remove;当线程没有结束,但是ThreadLocal已经被回收这种情况下就会出现  null 和object的键值对!!!

转:https://www.cnblogs.com/coshaho/p/5127135.html

2.2 什么是ThreadLocal

  • ThreadLocal,线程本地变量。ThreadLocal为变量在每个线程中创建了一个副本,那么每个线程可以访问自己的线程。 (get 方法获取到自己的线程的local变量)
  • 每个线程都可以独立的改变他们的副本不会影响到别的线程

get源码:

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();
    }

很好理解,就是先获取到当前线程,然后调取:

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

t.threadLocals其实就是

ThreadLocal.ThreadLocalMap threadLocals = null;

去找它对应的ThreadLocalMap,也就是2.1所在讲到的,ThreadLocalMap

更准确讲应该是因为t本身就是一个Thread,所以找它内部的成员属性就好~

看源码真是整洁,用this啊 包括一些泛型的使用,还有就是整体的封装性真的高!

2.3 setInitialValue()

 /**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

调用get最后都要调用这个。

补充:

 protected T initialValue() {
        return null;
    }

如果没有获取到map,那么创建一个

 /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

2.4 其他使用场景

2.4.1 数据库连接

private static ThreadLocal<Connection> connectionHolder= new ThreadLocal<Connection>() {
    public Connection initialValue() {
        return DriverManager.getConnection(DB_URL);
    }
};
 
public static Connection getConnection() {
    return connectionHolder.get();
}

2.4.2 Session管理

private static final ThreadLocal threadSession = new ThreadLocal();
 
public static Session getSession() throws InfrastructureException {
    Session s = (Session) threadSession.get();
    try {
        if (s == null) {
            s = getSessionFactory().openSession();
            threadSession.set(s);
        }
    } catch (HibernateException ex) {
        throw new InfrastructureException(ex);
    }
    return s;
}

延伸--Thread同步机制
ThreadLocal和线程的同步机制都是为了解决多线程在中相同变量的访问冲突,在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量,这时该变量是多个线程共享的,使用同步机制要求程序=缜密的分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写的难度相对较大。而ThreadLocal则从另一个角度来解决多线程的并发访问,Threadlocal会为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突,因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。
同步机制【以时间换空间】;ThreadLocal【以空间换时间】(毕竟要存很多副本! 这个思想很好 要记下 面试会被问大概)

其他使用场景 转自:https://blog.csdn.net/eieiei438/article/details/87805608

以前的ThreadLocal get()方法等获得的是Object对象,需要进行强制类型转换,那么通过JDK5.0就很好的解决了这个问题。

通过泛型就可以。

2.5 其他不错的博客讲解ThreadLocal

https://www.jianshu.com/p/4e1fcdfb6d54 

有空再看深层的原理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值