ThreadLocal源码解析

来源:互联网 发布:淘宝运营管理制度 编辑:程序博客网 时间:2024/06/06 05:55
 ThreadLocal不是一个具体的线程。它是一个线程内部的数据存储类,通过它可以再指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其它线程来说则无法获取到数据。
  ThreadLocal之所以有这么神奇的效果,是因为不同线程访问同一个ThreadLocal的get方法,ThreadLocal内部会将各自线程的引用当做table数组的一个值存在,然后从数组中根据当前ThreadLocal的reference去查找出相应的value。这就是为什么通过ThreadLocal可以再不同线程中维护一套数据的副本并且彼此互不干扰。
  在java中ThreadLocal以Map的形式存储数据(ThreadLocal对象为 key  数值为value)。在Android中做了些改动,在Thread-Local的add方法中,可以看到它会把ThradLocal对象(key)和相对应的value放在table数组连续的位置中。 也就是table被设计为下标为0,2,4…2n的位置存放key,而1,3,5…(2n +1 )的位置存放value。
[java] view plain copy
print?
  1. void add(ThreadLocal<?> key, Object value) {  
  2.             for (int index = key .hash & mask ;; index = next(index )) {  
  3.                 Object k = table[ index];  
  4.                 if (k == null) {  
  5.                     table[ index] = key. reference;  
  6.                     table[ index + 1] = value;  
  7.                     return;  
  8.                 }  
  9.             }  
  10.         }  
void add(ThreadLocal<?> key, Object value) {            for (int index = key .hash & mask ;; index = next(index )) {                Object k = table[ index];                if (k == null) {                    table[ index] = key. reference;                    table[ index + 1] = value;                    return;                }            }        }

类中最重要的两个方法是get(),set()。下面开始分析set()源码。
[java] view plain copy
print?
  1. public void set(T value ) {  
  2.     Thread currentThread = Thread.currentThread();  
  3.     Values values = values( currentThread);  
  4.     if (values == null) {  
  5.         values = initializeValues(currentThread );  
  6.     }  
  7.     values.put( this, value );  
  8. }  
    public void set(T value ) {        Thread currentThread = Thread.currentThread();        Values values = values( currentThread);        if (values == null) {            values = initializeValues(currentThread );        }        values.put( this, value );    }
首先获取当前线程对象currentThread,然后执行values(currentThread)方法。源码如下:
[java] view plain copy
print?
  1. Values values(Thread current) {  
  2.       return current .localValues;  
  3.   }  
Values values(Thread current) {      return current .localValues;  }
     在values (currentThread )中返回了currentThread .localValues。跟进Thread的源码可以发现:这个currentThread .localValues其实就是ThreadLocal.Values  localValues 。Values 是ThreadLocal中的一个静态内部类。此时获取到返回的Values对象。
     接下来进行判空操作,如果返回的values为空,那么再次实例化currentThread。跟进initializeValue-s(currentThread)可以发现
[java] view plain copy
print?
  1. Values initializeValues(Thread current) {  
  2.         return current .localValues = new Values();  
  3.     }  
  4.   
  5. new Values()的实例化过程:  
  6.   
  7.        Values() {  
  8.             initializeTable( INITIAL_SIZE);//INITIAL_SIZE 默认值16  
  9.             this.size = 0;  
  10.             this.tombstones = 0;  
  11.         }  
  12.   
  13.       private void initializeTable( int capacity ) {  
  14.             this.table = new Object[capacity * 2];  
  15.             this.mask = table .length - 1;  
  16.             this.clean = 0;  
  17.             this.maximumLoad = capacity * 2 / 3// 2/3  
  18.         }  
Values initializeValues(Thread current) {        return current .localValues = new Values();    }new Values()的实例化过程:       Values() {            initializeTable( INITIAL_SIZE);//INITIAL_SIZE 默认值16            this.size = 0;            this.tombstones = 0;        }      private void initializeTable( int capacity ) {            this.table = new Object[capacity * 2];            this.mask = table .length - 1;            this.clean = 0;            this.maximumLoad = capacity * 2 / 3; // 2/3        }
此时可以确保有了Values的一个实例,接下来就可以执行values .put(this, value ),跟进put方法
[java] view plain copy
print?
  1. void put(ThreadLocal<?> key, Object value) {  
  2.             cleanUp();  
  3.   
  4.             // Keep track of first tombstone. That’s where we want to go back  
  5.             // and add an entry if necessary.  
  6.             int firstTombstone = -1;  
  7.   
  8.             for (int index = key .hash & mask ;; index = next(index )) {  
  9.                 Object k = table[ index];  
  10.   
  11.                 if (k == key .reference ) {  
  12.                     // Replace existing entry.  
  13.                     table[ index + 1] = value;  
  14.                     return;  
  15.                 }  
  16.   
  17.                 if (k == null) {  
  18.                     if (firstTombstone == -1) {  
  19.                         // Fill in null slot.  
  20.                         table[ index] = key.reference ;  
  21.                         table[ index + 1] = value;  
  22.                         size++;  
  23.                         return;  
  24.                     }  
  25.   
  26.                     // Go back and replace first tombstone.  
  27.                     table[ firstTombstone] = key.reference ;  
  28.                     table[ firstTombstone + 1] = value;  
  29.                     tombstones–;  
  30.                     size++;  
  31.                     return;  
  32.                 }  
  33.   
  34.                 // Remember first tombstone.  
  35.                 if (firstTombstone == -1 && k == TOMBSTONE) {  
  36.                     firstTombstone = index ;  
  37.                 }  
  38.             }  
  39.         }  
void put(ThreadLocal<?> key, Object value) {            cleanUp();            // Keep track of first tombstone. That's where we want to go back            // and add an entry if necessary.            int firstTombstone = -1;            for (int index = key .hash & mask ;; index = next(index )) {                Object k = table[ index];                if (k == key .reference ) {                    // Replace existing entry.                    table[ index + 1] = value;                    return;                }                if (k == null) {                    if (firstTombstone == -1) {                        // Fill in null slot.                        table[ index] = key.reference ;                        table[ index + 1] = value;                        size++;                        return;                    }                    // Go back and replace first tombstone.                    table[ firstTombstone] = key.reference ;                    table[ firstTombstone + 1] = value;                    tombstones--;                    size++;                    return;                }                // Remember first tombstone.                if (firstTombstone == -1 && k == TOMBSTONE) {                    firstTombstone = index ;                }            }        }

 依据上面的代码可以得出一个存储规则:ThreadLocal的值在table数组中的存储位置总是为reference字段所表示的对象的下一个位置。
table[index] =key.reference;
table[index+ 1] =value;
最终ThreadLocal的值会被存储在table数组中:table[index+ 1] =value;至此,set方法解析完毕。下面看一下get方法。

[java] view plain copy
print?
  1. public T get() {  
  2.         // Optimized for the fast path.  
  3.         Thread currentThread = Thread.currentThread();  
  4.         Values values = values( currentThread);  
  5.         if (values != null) {  
  6.             Object[] table = values. table;  
  7.             int index = hash & values .mask ;  
  8.             if (this .reference == table [index ]) {  
  9.                 return (T) table [index + 1];  
  10.             }  
  11.         } else {  
  12.             values = initializeValues(currentThread );  
  13.         }  
  14.   
  15.         return (T) values .getAfterMiss(this);  
  16.     }  
public T get() {        // Optimized for the fast path.        Thread currentThread = Thread.currentThread();        Values values = values( currentThread);        if (values != null) {            Object[] table = values. table;            int index = hash & values .mask ;            if (this .reference == table [index ]) {                return (T) table [index + 1];            }        } else {            values = initializeValues(currentThread );        }        return (T) values .getAfterMiss(this);    }
看完set方法后再看get就比较简单了,首先得到一个Values对象,然后求出table数组ThreadLocal.reference的下标。前文说过:ThradLocal对象(key)和相对应的value放在table数组连续的位置中。 也就是table被设计为下标为0,2,4…2n的位置存放key,而1,3,5…(2n +1 )的位置存放value。现在得到index后再index+1就是value在table数组中的下标。即value=table[index+1];return value即可。
到此想必读者对ThreadLocal为什么能在不同线程中能够为不同线程创建不同的线程副本(其实不太准确,应该是相同对象的不同值),原因就在于采用了key value形式的table数组。key为不同线程的reference,value就五花八门了。

        ThreadLocal浅析到此结束。谢谢欣赏~

更多Framework源码解析,请移步 Framework源码解析系列[目录]





原创粉丝点击