• ThreadLocal学习


    1、简介:

    类ThreadLocal<T>,为变量提供了线程本地化副本。对于用ThreadLocal维护的变量,当前线程中的副本不同于它在其他线程中的副本,每个线程通过ThreadLocal提供的get、set等方法来独立维护自己的变量副本。当多线程环境中的变量使用ThreadLocal维护时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本。

    ThreadLocal实例在类中通常作为静态私有化类变量。

    2、ThreadLocal接口:

    public T get(); //返回被维护变量的变量的线程本地化的副本值
    public void set(T value); //将当前线程中维护的本地化副本值设置为value
    public void remove(); //移除当前线程中被维护变量的值

    protected T initialValue(); //提供线程本地化对象的初始值

    3、适用性:

    ThreadLocal适用于为每个线程都提供一个固定的初始状态的情形,每个线程使用自己的本地化对象来完成自己的操作。

    4、Demo:

    对非线程安全对象的改造:

     1      public class TopicDao{
     2           //一个非线程安全的变量
     3           private Connection conn;
     4 
     5           public void addTopic(){
     6               conn = DriverManager.getConnection();
     7 
     8             //在多线程环境中使用非线程安全变量
     9             Statement stat = conn.createStatement();
    10             
    11           }
    12      }

    在使用JDBC来操作数据库时,Connection对象提供了所连接的数据库的表、支持的SQL语句等信息,但是Connection本身并不是一个线程安全变量,用于多线程环境中可能会出现问题。

    改造: 使用ThreadLocal来为每个线程维护一个conn变量的线程本地化副本。

     1      public class TopicDao{
     2           //使用ThreadLocal来保存Connection变量
     3           private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();
     4           public static Connection getConnection(){
     5                //如果connThreadLocal没有本线程对应的Connection,创建一个新的Connection,
     6                //并将其保存到线程本地变量中
     7                if(connThreadLocal.get() == null ){
     8                     Connection conn = ConnectionManager.getConnection();
     9                     connThreadLocal.set(conn);
    10                     return conn;
    11                }else{
    12                     //直接返回线程本地变量
    13                     return connThreadLocal.get();
    14                }
    15           }
    16 
    17           public void addTopic(){
    18                //从ThreadLocal中获取线程对象
    19                Statement stat = getConnection().createStatement();
    20           }
    21      }

    ThreadLocal使用方式

    由例子中也可看出,ThreadLocal实例通常作为类的私有静态成员,并且提供一个静态方法来返回ThreadLocal所维护的线程本地化对象(该例中未conn)的值,同时在该静态方法中为每个线程提供该线程本地化对象的初始值。

    5、ThreadLocal.java源文件:

      1     /**
      2     * ThreadLocal内部包含一个用数组实现的哈希表,用来存储对应到每个线程的局部对象的值
      3     * 其中,ThreadLocal对象担当key,实际通过threadLocalHashCode值来进行检索
      4     */
      5        public class ThreadLocal<T> {
      6            /**
      7            * 存储用于确定哈希表的键值
      8            */
      9            private final int threadLocalHashCode = nextHashCode();
     10 
     11            /** 
     12            * 返回在当前线程中的线程局部对象的值, 
     13            * 若线程局部对象对于当前线程没有值,则被初始化微 initialValue方法的返回值 
     14         */ 
     15         public T get() { 
     16               Thread t = Thread.currentThread(); 
     17               ThreadLocalMap map = getMap(t); 
     18               if (map != null) { 
     19                   ThreadLocalMap.Entry e = map.getEntry(this); 
     20                   if (e != null) 
     21                       return (T)e.value; 
     22               } 
     23   
     24                   return setInitialValue(); 
     25           } 
     26   
     27           /** 
     28          * 获取当前线程对应的ThreadLocalMap对象
     29          * 若存在,则将当前线程与T类型变量value按照名值对的方式放置到ThreadLocalMap中;
     30          * 若不存在,则创建当前ThreadLocalMap对象,并放入
     31           */ 
     32           public void set(T value) { 
     33               Thread t = Thread.currentThread(); 
     34               ThreadLocalMap map = getMap(t); 
     35               if (map != null) 
     36               //以当前线程对象为key,设置当前局部对象的值
     37               map.set(this, value); 
     38               else 
     39               createMap(t, value); 
     40           } 
     41   
     42           /** 
     43           * 移除当前线程中所保存的本地化对象的值
     44           */ 
     45           public void remove() { 
     46               ThreadLocalMap m = getMap(Thread.currentThread()); 
     47               if (m != null) 
     48                   //从ThreadLocalMap中移除对象
     49                   m.remove(this); 
     50           } 
     51   
     52           /** 
     53           * 返回当前线程关联的ThreadLocalMap对象
     54           * 查看Thread源码可知,每个Thread都含有ThreadLocal.ThreadLocalMap类型的threadLocals变量
     55           */ 
     56           ThreadLocalMap getMap(Thread t) { 
     57           return t.threadLocals; 
     58           } 
     59   
     60           /** 
     61           * 创建当前线程关联的ThreadLocalMap对象
     62           */
     63           void createMap(Thread t, T firstValue) { 
     64           t.threadLocals = new ThreadLocalMap(this, firstValue);
     65           } 
     66   
     67          /**
     68          * ThreadLocalMap是一个定制的只能用来存储线程局部对象的哈希映射表
     69          *
     70          * 使用数组来作为哈希表存储结构
     71         *
     72          * 其中数组中元素类型为Entry,Entry为指向ThreadLocal的弱引用的子类,并且含有Object变量来维护与当前ThreadLocal对象关联的变量值
     73          *
     74          */
     75         static class ThreadLocalMap { 
     76               
     77             static class Entry extends WeakReference<ThreadLocal> { 
     78                 /** 
     79                 * 维护与当前ThreadLocal相关联的变量值 
     80                 */ 
     81                  Object value; 
     82      
     83                  Entry(ThreadLocal k, Object v) { 
     84                   super(k); 
     85                   value = v; 
     86                  } 
     87             } 
     88      
     89             //哈希表的初始大小 
     90             private static final int INITIAL_CAPACITY = 16; 
     91      
     92              /** 
     93              * 哈希表存储的数组
     94              * 
     95              * 其中数组中元素类型为Entry,Entry为指向ThreadLocal的弱引用的子类,并且含有Object变量来维护与当前ThreadLocal对象关联的变量值
     96              */ 
     97              private Entry[] table; 
     98      
     99              /**
    100              * ThreadLocalMap使用延迟初始化,当我们需要向ThreadLocalMap中放元素时,才会初始化它 
    101              */
    102              ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { 
    103                  table = new Entry[INITIAL_CAPACITY]; 
    104                  int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); //产生哈希值
    105                  table[i] = new Entry(firstKey, firstValue); //初始时,使用ThreadLocal.threadLocalHashCode作为哈希表的哈希值 size = 1; 
    106      
    107                  //设定ThreadLocalMap中元素个数 
    108                  setThreshold(INITIAL_CAPACITY); 
    109              } 
    110      
    111             /** 
    112             * 设置于当前key所关联的线程本地化对象的值
    113             *
    114             * 涉及到哈希表的冲突处理
    115             */ 
    116              private void set(ThreadLocal key, Object value) { 
    117                  Entry[] tab = table; 
    118                  int len = tab.length; 
    119                  int i = key.threadLocalHashCode & (len-1); 
    120                  for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { 
    121                      ThreadLocal k = e.get(); 
    122                      if (k == key) { 
    123                          e.value = value; 
    124                          return; 
    125                      } 
    126      
    127                      if (k == null) { 
    128                          replaceStaleEntry(key, value, i); 
    129                          return;
    130                      } 
    131                  } 
    132      
    133                  tab[i] = new Entry(key, value); 
    134                  int sz = ++size; 
    135                  if (!cleanSomeSlots(i, sz) && sz >= threshold) 
    136                      rehash(); 
    137              } 
    138      
    139              /** 
    140              * 删除ThreadLocal对应的线程本地化对象的值
    141              */ 
    142              private void remove(ThreadLocal key) { 
    143                 Entry[] tab = table; 
    144                 int len = tab.length; 
    145                 int i = key.threadLocalHashCode & (len-1); 
    146                 for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { 
    147                       if (e.get() == key) { 
    148                         e.clear(); 
    149                         expungeStaleEntry(i); 
    150                         return; 
    151                     } 
    152                 } 
    153             } 
    154         }
    155     }

    根据ThreadLocal.java中列出的源码,可以走一遍ThreadLocal是如何进行set、get、remove操作的,可以加深对ThreadLocal对象的理解和其内部工作机制。

    注:查看Thread源码可知,每个Thread类中都含有ThreadLocal.ThreadLocalMap类型的threadLocals变量(初始值为null)。

  • 相关阅读:
    代码习惯
    全网最详细的fhq treap (非旋treap)讲解
    按位或「HAOI2015」
    列队「NOIP2017」
    愤怒的小鸟「NOIP2016」
    能量传输「CSP多校联考 2019」
    矿物运输「CSP多校联考 2019」
    普通打击「CSP多校联考 2019」
    普通快乐「CSP多校联考 2019」
    BZOJ4385: [POI2015]Wilcze doły
  • 原文地址:https://www.cnblogs.com/-crazysnail/p/3923093.html
Copyright © 2020-2023  润新知