我在之前的文章《Android中的Looper,Handler及HandlerThread简析》(http://maosidiaoxian.iteye.com/blog/1927735)中有提到过ThreadLocal,后来一直想详细读读这个类。前几天写完了Java原生的ThreadLocal,今天来看一下Android中的ThreadLocal类。在读这篇文章之前,建议先读一下我前面写的关于Java中的ThreadLocal解析的两篇文章,因为在这里对一些相同的内容我会不再赘述。
Android中的ThreadLocal的源码在libcore/luni/src/main/java/java/lang目录下,如果你没有下载Android源码,可通过以下地址读到该类的源码:https://android.googlesource.com/platform/libcore/+/android-4.3_r2.2/luni/src/main/java/java/lang/ThreadLocal.java
在Android中,ThreadLocal像是对原来的ThreadLcal做了优化的实现。我们同样先看看这个类的结构,如下图:
可以直观地看到在android中ThreadLocal比java原生的这个类少了一些API,而且保存线程变量的内部类名字也改为Values,里面没有再定义内部类。仔细地阅读比较,我们可以看到Android中对Java原生的ThreadLocal做了一些优化的工作。
先来看看ThreadLocal的变量,代码如下:
/** Weak reference to this thread local instance. */
private final Reference<ThreadLocal<T>> reference
= new WeakReference<ThreadLocal<T>>(this);
/** Hash counter. */
private static AtomicInteger hashCounter = new AtomicInteger(0);
/**
* Internal hash. We deliberately don't bother with #hashCode().
* Hashes must be even. This ensures that the result of
* (hash & (table.length - 1)) points to a key and not a value.
*
* We increment by Doug Lea's Magic Number(TM) (*2 since keys are in
* every other bucket) to help prevent clustering.
*/
private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);
在这里,ThreadLocal没有再定义HASH_INCREMENT 这个常量,而是直接写在了hash变量的定义当中。将原来的nextHashCode的代码与hash的定义合在了一起,增量则是0x61c88647 * 2。
在这里还多定义了一个变量,Reference<ThreadLocal<T>> reference,它是一个弱引用,引用ThreadLocal实例的自己。
而当实例化一个ThreadLocal对象时,仅仅是生成一个hash值,和对reference赋值。
在set方法中,依然是从线程中取得保存变量的对象,在这里是values,如果values为null就进行初始化(对thread对象创建Values对象并返回),然后调用其put方法保存变量,与Java原生的思路都是一样的,只是代码简化了许多,如下:
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
/**
* Gets Values instance for this thread and variable type.
*/
Values values(Thread current) {
return current.localValues;
}
/**
* Creates Values instance for this thread and variable type.
*/
Values initializeValues(Thread current) {
return current.localValues = new Values();
}
remove()方法就不谈了,很简单,跟原来的基本一致。
下面来看一下get()方法。
/**
* Returns the value of this variable for the current thread. If an entry
* doesn't yet exist for this variable on this thread, this method will
* create an entry, populating the value with the result of
* {@link #initialValue()}.
*
* @return the current value of the variable for the calling thread.
*/
@SuppressWarnings("unchecked")
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);
}
我们会注意到其中的代码:
if (this.reference == table[index]) {
return (T) table[index + 1];
}
为什么用hash取得下标后,下一位才是保存的变量值呢?
我们来看一下Values这个类是怎么定义和设计的。
Values是被设计用来保存线程的变量的一个类,它相当于一个容器,存储保存进来的变量。它的成员变量如下:
/**
* Map entries. Contains alternating keys (ThreadLocal) and values.
* The length is always a power of 2.
*/
private Object[] table;
/** Used to turn hashes into indices. */
private int mask;
/** Number of live entries. */
private int size;
/** Number of tombstones. */
private int tombstones;
/** Maximum number of live entries and tombstones. */
private int maximumLoad;
/** Points to the next cell to clean up. */
private int clean;
同样table是实际上保存变量的地方,但它在这里是个Object类型的数组,它的长度必须是2的n次方的值。mask即计算下标的掩码,它的值是table的长度-1。size表示存放进来的实体的数量。这与前面原生的ThreadLocal的ThreadLocalMap是一样的。但是在这里它还定义了三个int类型的变量:tombstones表示被删除的实体的数量,maximumLoad是一个阈值,用来判断是否需要进行rehash,clean表示下一个要进行清理的位置点。
我们来看一下当Values对象被创建时进行了什么工作,代码如下:
/**
* Constructs a new, empty instance.
*/
Values() {
initializeTable(INITIAL_SIZE);
this.size = 0;
this.tombstones = 0;
}
/**
* Creates a new, empty table with the given capacity.
*/
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对象时,它会创建一个长度为capacity*2的数组。
然后在add()方法当中,也可以看到它会把ThreadLocal对象(key)和对应的value放在连续的位置中。
/**
* Adds an entry during rehashing. Compared to put(), this method
* doesn't have to clean up, check for existing entries, account for
* tombstones, etc.
*/
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;
}
}
}
也就是table被设计为下标为0,2,4...2n的位置存放key,而1,3,5...(2n +1 )的位置存放value。直接通过下标存取线程变量,它比用WeakReference<ThreadLocal>类在内存占用上更经济,性能也更好。这也是前面中hash的增量要取0x61c88647*2的原因,它也保证了其二进制中最低位为0,也就是在计算key的下标时,一定是偶数位。
而在remove()方法中,移除变量时它是把对应的key的位置赋值为TOMBSTONE,value赋值为null,然后 tombstones++;size--;。TOMBSTONE是前面定义的一个常量,表示被删除的实体。
其他方法的算法,其实与Java原生的一样,只是做了对应于Values类的设计的修改。这里不再赘述。
- 大小: 9.5 KB
- 大小: 13.4 KB
分享到:
相关推荐
ThreadLocal源码(版本:Android4.3,,含注释)
threadlocal源码解析
Android 中 ThreadLocal 的深入理解 Android 中 ThreadLocal 的深入理解是指在 Android 开发中对 ThreadLocal 的深入理解和应用。ThreadLocal 是 Java 语言中的一种线程内部数据存储机制,通过它可以在指定的线程中...
在Android中,ThreadLocal常被用来存储线程相关的数据,例如Handler中的Looper对象,因为Looper与线程紧密关联,每个线程通常只有一个Looper,而ThreadLocal正好满足这种需求。 下面是一个简单的ThreadLocal使用...
4. **注意线程池中的ThreadLocal**:线程池中的线程可能会被重用,若不清理ThreadLocal,可能导致后续任务访问到错误的变量副本。 通过理解ThreadLocal的原理和最佳实践,我们可以更有效地利用它来解决多线程环境下...
首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。
Java并发编程学习之ThreadLocal源码详析 ThreadLocal是Java并发编程中的一种机制,用于解决多线程访问共享变量的问题。它可以使每个线程对共享变量的访问都是线程安全的,使得多线程编程变得更加简单。 ...
下面将详细介绍ThreadLocal的工作原理、使用方法以及在Android中的实际应用。 ### 1. ThreadLocal工作原理 ThreadLocal内部实现了一个HashMap,用于存储每个线程与对应的变量副本之间的映射关系。当我们创建一个新...
ThreadLocal源码以及应用
ThreadLocal 源码分析和使用 ThreadLocal 是 Java 语言中的一种多线程编程机制,用于解决多线程程序的并发问题。它不是一个 Thread,而是一个 Thread 的局部变量。ThreadLocal 的出现是为了解决多线程程序中的共享...
ThreadLocal是Java编程语言中的一个类,用于在多线程环境下提供线程局部变量。它为每个线程创建了一个独立的变量副本,每个线程只能访问自己的副本,不会影响其他线程。ThreadLocal的设计思想是解决共享数据在多线程...
ThreadLocal的作用是提供线程内的局部变量,这种变量在多线程环境下访问时能够保证各个线程里变量的独立性。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是...
ThreadLocal是Java编程语言中的一个类,用于在多线程环境中提供线程局部变量。它是一种特殊类型的变量,每个线程都有自己的副本,互不影响,从而实现线程间数据隔离。ThreadLocal通常被用来解决线程共享数据时可能...
Java源码解析ThreadLocal及使用场景 ThreadLocal是Java中一个非常重要的类,它在多线程环境下经常使用,用于提供线程本地变量。这些变量使每个线程都有自己的一份拷贝,使得多个线程可以独立地使用变量,不会彼此...
Java中ThreadLocal工具类(解决多线程程序中并发问题的一种新思路,主要为参数的拷贝问题),感兴趣的话可以查看博文,博文地址:http://blog.csdn.net/otengyue/article/details/38459327
ThreadLocal类的作用:为每个线程创建独立的副本,从而保证了线程安全。 ThreadLocal使用代码示例: public class MyThreadLocalTest { private ThreadLocal threadLocal=new ThreadLocal(){ @Override protected...
### Java中ThreadLocal详解 #### 一、ThreadLocal概述 在Java多线程编程中,`ThreadLocal`是一个非常重要的工具类,它提供了一种在每个线程内部存储线程私有实例的方法。通常情况下,当多个线程共享某个变量时,...
《ThreadPoolExecutor源码解析》 ThreadPoolExecutor是Java并发编程中重要的组件,它是ExecutorService接口的实现,用于管理和调度线程的执行。理解其源码有助于我们更好地控制并发环境下的任务执行,提高系统的...