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。
- 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;
- }
- }
- }
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()源码。
- public void set(T value ) {
- Thread currentThread = Thread.currentThread();
- Values values = values( currentThread);
- if (values == null) {
- values = initializeValues(currentThread );
- }
- values.put( this, value );
- }
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)方法。源码如下:
- Values values(Thread current) {
- return current .localValues;
- }
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)可以发现
- 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 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方法
- 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 ;
- }
- }
- }
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方法。
- 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);
- }
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浅析到此结束。谢谢欣赏~
阅读全文
0 0
- ThreadLocal源码解析
- ThreadLocal源码解析
- ThreadLocal源码解析
- ThreadLocal源码解析
- ThreadLocal源码解析
- ThreadLocal类源码解析
- ThreadLocal源码解析
- threadlocal源码解析
- ThreadLocal源码解析
- ThreadLocal源码解析(二)
- ThreadLocal源码解析
- ThreadLocal 源码解析
- ThreadLocal源码解析
- ThreadLocal源码解析
- ThreadLocal源码解析
- Java 源码解析-ThreadLocal
- ThreadLocal源码解析
- Java中的ThreadLocal源码解析
- HDOJ1016 Prime Ring Problem(基础DFS)
- PHP中表单提交后,select中的值不清空
- 树链剖分人工栈模板
- NYOJ 36 最长公共子序列 模板题
- 了解checkbox按钮
- ThreadLocal源码解析
- 包含目录、库目录、附加包含目录、附加库目录、附加依赖项之详解
- weex 第一个小程序
- Linux(CentOS 6.5) 安装redis
- Person中有name和age属性,要求存储若干个Person对象到磁盘中, 使用 RandomAccessFile流来实现
- JavaScript基础
- BZOJ 2698: 染色 期望
- image和TFRecord互相转换
- 646. Maximum Length of Pair Chain思路解析