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)。

posted @ 2014-08-19 21:24  _crazysnail_  阅读(504)  评论(0编辑  收藏  举报