学习ThreadLocal源码心得

来源:互联网 发布:箩筐软件怎么用 编辑:程序博客网 时间:2024/06/04 18:23

一、概念

JDK给出的ThreadLocal定义:

This class provides thread-local variables. These variables differ from their normalcounterparts in that each thread that accesses one (via itsget orset method) hasits own, independently initialized copy of the variable.ThreadLocal instances are typically private static fieldsin classes that wish to associate state with a thread (e.g., a user ID orTransaction ID).

ThreadLocal<T>维护线程的本地变量。这些变量不同于正常的变量,每个线程访问一个(通过getset方法)有自己独立初始化的变量的副本。ThreadLocal实例经常用在状态与线程紧密关联的私有静态属性上。

三、ThreadLocal源码

每个Thread对象中都有一个ThreadLocalMap对象,它属于Map类型,其中key为ThreadLocal对象,value为某个对象。

ThreadLocalMap对象是定义在ThreadLocal的静态内部类。

先了解一下ThreadLocal提供的几个方法和一个ThreadLocalMap内部类

Get方法:从当前线程的ThreadLocalMap中取出 key为当前ThreadLocal对象 的value对象,其实key值与ThreadLocal的threadLocalHashCode值有关  

   /**     * 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 map = getMap(t);//得到当前线程的ThreadLocalMap对象        if (map != null) {            ThreadLocalMap.Entry e = map.getEntry(this);//得到map中Entry实体对象;            if (e != null) {                @SuppressWarnings("unchecked")                T result = (T)e.value;                return result;            }        }        return setInitialValue();//如果map为null,则创建ThreadLocalMap对象,并且创建一个空的T对象放到map中,最后返回null      }
Set方法:把value放到当前线程的ThreadLocalMap对象中去,其中key值与当前ThreadLocal对象的threadLocalHashCode值有关  

<span style="white-space:pre"></span>/**     * Sets the current thread's copy of this thread-local variable     * to the specified value.  Most subclasses will have no need to     * override this method, relying solely on the {@link #initialValue}     * method to set the values of thread-locals.     *     * @param value the value to be stored in the current thread's copy of     *        this thread-local.     */    public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }

二、Spring事务中的ThreadLocal应用

数据库链接管理类,在多线程中使用会存在线程安全问题:第一,由于connect是共享变量,那么必然在调用connect的地方需要使用到同步来保障线程安全,因为很可能一个线程在使用connect进行数据库操作,而另外一个线程调用closeConnection关闭链接。第二,使用锁,会影响并发。
Spring事务中的TransactionSynchronizationManager使用ThreadLocal的方法为每一个线程建立一个数据库连接副本。

静态属性resources:    

Key:是dataSource   Value:Connection

目地为每一个线程绑定一个连接,当然里面还涉及到了事务的conne-passing原理(这个先不深入讲了)

private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<Map<Object, Object>>("Transactional resources");

Set方法:

<span style="white-space:pre"></span>/** * Bind the given resource for the given key to the current thread. * @param key the key to bind the value to (usually the resource factory) * @param value the value to bind (usually the active resource object) * @throws IllegalStateException if there is already a value bound to the thread * @see ResourceTransactionManager#getResourceFactory() */public static void bindResource(Object key, Object value) throws IllegalStateException {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);Assert.notNull(value, "Value must not be null");Map<Object, Object> map = resources.get();// set ThreadLocal Map if none found<span style="white-space:pre"></span>if (map == null) {map = new HashMap<Object, Object>();resources.set(map);}Object oldValue = map.put(actualKey, value);// Transparently suppress a ResourceHolder that was marked as void...if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {oldValue = null;}if (oldValue != null) {throw new IllegalStateException("Already value [" + oldValue + "] for key [" +actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");}if (logger.isTraceEnabled()) {logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" +Thread.currentThread().getName() + "]");}}

Get方法:

<span style="white-space:pre"></span>/** * Actually check the value of the resource that is bound for the given key. */private static Object doGetResource(Object actualKey) {Map<Object, Object> map = resources.get();if (map == null) {return null;}Object value = map.get(actualKey);// Transparently remove ResourceHolder that was marked as void...if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {map.remove(actualKey);// Remove entire ThreadLocal if empty...if (map.isEmpty()) {resources.remove();}value = null;}return value;}





0 0