这个类Android中直接用到的场景不是很多,几乎都间接用到了,比如Looper中就会用到;
ThreadLocal看名字感觉像是一个线程,其实并不是,就是一个普通的类,但是确实跟线程有关系。
那么有什么作用呢?
ThreadLocal不是用来解决对象共享访问问题的,而主要是提供了线程保持对象的方法和避免参数传递的方便的对象访问方式 ,
ThreadLocal的应用场合,最适合的是按线程多实例(每个线程对应一个实例)的对象的访问,并且这个对象很多地方都要用到。
说白了就是帮助每个线程有自己独立的一个对象,所以就不存在共享的问题,但是这个对象必须都不同,不然还是会有共享并发的问题。
ThreadLocal有set、get、remove三个public方法,initialValue一个protected方法,其实先不看分析,就看名字大概也猜得出是干嘛的,set添加一个对象,get获取这个对象,
remove删除这个对象,initialValue初始化一个默认对象。
现在分析下这几个方法吧
1、get
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
先获取到当前线程对象,然后用这个线程对象获取到一个ThreadLocalMap对象,如果map不为空,用this作为key获取对象的value,如果map为空则初始化一个value,
看下getMap(t)
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
看到了吧,把当前这个线程对象所有持有的threadLocals返回,每个线程对象都有一个ThreadLocalMap的引用,那么TheadLocalMap是何方神圣呢?
/**
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread. To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.
*/
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
其实它是ThreadLocal的一个静态内部类,说白了就是自定义的一个map,里面的具体代码就不看了,知道它是一个map就可以了,接着往下看setInitialValue()
/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
/**
* Returns the current thread's "initial value" for this
* thread-local variable. This method will be invoked the first
* time a thread accesses the variable with the {@link #get}
* method, unless the thread previously invoked the {@link #set}
* method, in which case the {@code initialValue} method will not
* be invoked for the thread. Normally, this method is invoked at
* most once per thread, but it may be invoked again in case of
* subsequent invocations of {@link #remove} followed by {@link #get}.
*
* <p>This implementation simply returns {@code null}; if the
* programmer desires thread-local variables to have an initial
* value other than {@code null}, {@code ThreadLocal} must be
* subclassed, and this method overridden. Typically, an
* anonymous inner class will be used.
*
* @return the initial value for this thread-local
*/
protected T initialValue() {
return null;
}
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
如果map为null,就会调用setInitialValue(),initialValue()返回的也是null,然后存在了map里面,key呢就是当前ThreadLocal对象,当然initialValue()是可以重写的,初始化一个默认对象
是不是发现了啥,接下来看下set()
2、set
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
首先获取当前线程对象,然后getMap(t)获取线程对象的ThreadLocal.ThreadLocalMap threadLocals,然后这个map把value以键值对的形式存进去,如果map为null,创建一个map再存进去
3、remove
/**
* Removes the current thread's value for this thread-local
* variable. If this thread-local variable is subsequently
* {@linkplain #get read} by the current thread, its value will be
* reinitialized by invoking its {@link #initialValue} method,
* unless its value is {@linkplain #set set} by the current thread
* in the interim. This may result in multiple invocations of the
* {@code initialValue} method in the current thread.
*
* @since 1.5
*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
这个就更简单了,获取map,根据key删除对应的value
是不是很简单呢?逻辑就这点了,不算复杂,使用也简单,一起来看下Android中Looper的具体使用吧
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
Looer中先初始化了一个静态的ThreadLocal的对象,泛型是Looper,因为这个泛型就是以后存储的value,然后prepare初始化Looper时会用到ThreadLocal,
sThreadLocal.get() != null,一开始就通过get()获取当前线程中的Looper,如果不为null,就抛出异常说已经存在了,因为一个线程中只允许有一个Looper实例,
否则创建一个Looper实例存在线程中的ThreadLocalMap中。
好了,是不是很简单拉,又可以愉快玩耍了