zoukankan      html  css  js  c++  java
  • ThreadLocal详解

    ThreadLocal是为了使每个线程保存一份属于自己的数据。

    先看一个使用ThreadLocal的实例。

    package org.springframework.aop.framework;
    
    import org.springframework.core.NamedThreadLocal;
    
    public abstract class AopContext {
    
        private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<Object>("Current AOP proxy");
    
        public static Object currentProxy() throws IllegalStateException {
            Object proxy = currentProxy.get();
            if (proxy == null) {
                throw new IllegalStateException(
                        "Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.");
            }
            return proxy;
        }
    
        static Object setCurrentProxy(Object proxy) {
            Object old = currentProxy.get();
            if (proxy != null) {
                currentProxy.set(proxy);
            }
            else {
                currentProxy.remove();
            }
            return old;
        }
    
    }

    上例展示的是spring框架中获取当前线程的代理对象的方法,AopContext.currentProxy(),在本线程的程序调用栈中只要调用AopContext的静态方法就可以获取本线程相关的代理对象。如果不用ThreadLocal,那么这个代理对象在创建后,就要一层层传递下去,才能在后面获取到并使用。
    通过这个例子,我们可以知道[b]ThreadLocal主要是提供了一种保持对象的方法以及避免了对象在程序调用中传递的简便访问方法。ThreadLocal与共享数据和同步没有明显关系。[/b]

    下面看看相关的源码以了解具体的结构。

    public class Thread implements Runnable {
        // ThreadLocalMap是一个以ThreadLocal为key,Object为值的map,由ThreadLocal维护
            ThreadLocal.ThreadLocalMap threadLocals = null;
    }

    从Thread的源码中可以得知,是每一个Thread持有一个自己的map,并不是一个ThreadLocal持有一个map。

    public class ThreadLocal<T> {
    
     public T get() {
        // 获取当前线程
            Thread t = Thread.currentThread();
        // 获取当前线程的threadLocals变量
            ThreadLocalMap map = getMap(t);
        // 从当前线程的threadLocals变量中取得本threadLocal为key的值
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null)
                    return (T)e.value;
            }
            return setInitialValue();
        }
        
         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;
        }
        
       
         public void set(T value) {
        // 获取当前线程
            Thread t = Thread.currentThread();
        // 获取当前线程的threadLocals变量
            ThreadLocalMap map = getMap(t);
        // 以本threadLocal为key的保存值到当前线程的threadLocals变量中去
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
        
         ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
        
         void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }
    }

    使用ThreadLocal过程: 对于每一个需要线程保存自身实例的变量,需要定义一个静态的ThreadLocal实例。然后将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象。
    此外,每个线程保存的自身数据并不是通过备份或复制的,而是new创建出来的。

    通过ThreadLocal各个线程只能获取自身对应的数据,不能访问其他线程的数据,但是如果两个线程在set时引用了同一个数据,仍然存在同步问题。

  • 相关阅读:
    Python使用SMTP模块、email模块发送邮件
    harbor搭建及使用
    ELK搭建-windows
    ELK技术栈之-Logstash详解
    【leetcode】1078. Occurrences After Bigram
    【leetcode】1073. Adding Two Negabinary Numbers
    【leetcode】1071. Greatest Common Divisor of Strings
    【leetcode】449. Serialize and Deserialize BST
    【leetcode】1039. Minimum Score Triangulation of Polygon
    【leetcode】486. Predict the Winner
  • 原文地址:https://www.cnblogs.com/sage-blog/p/4070178.html
Copyright © 2011-2022 走看看