ThreadLocal
ThreadLocal在android中属于一个常用的类,比如的Android最重要的Handler消息机制里面的Looper存储也是采用ThreadLocal,开源框架EventBus存储当前线程下的发送事件队列状态也是采用ThreadLocal。
ThreadLocal到底能做什么呢?原理又是什么呢?
点进ThreadLocal的类文件看一下,类上写的注释。
This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable. {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
*
* <p>For example, the class below generates unique identifiers local to each
* thread.
* A thread's id is assigned the first time it invokes {@code ThreadId.get()}
* and remains unchanged on subsequent calls.
* <pre>
* import java.util.concurrent.atomic.AtomicInteger;
*
* public class ThreadId {
* // Atomic integer containing the next thread ID to be assigned
* private static final AtomicInteger nextId = new AtomicInteger(0);
*
* // Thread local variable containing each thread's ID
* private static final ThreadLocal<Integer> threadId =
* new ThreadLocal<Integer>() {
* @Override protected Integer initialValue() {
* return nextId.getAndIncrement();
* }
* };
*
* // Returns the current thread's unique ID, assigning it if necessary
* public static int get() {
* return threadId.get();
* }
* }
* </pre>
* <p>Each thread holds an implicit reference to its copy of a thread-local
* variable as long as the thread is alive and the {@code ThreadLocal}
* instance is accessible; after a thread goes away, all of its copies of
* thread-local instances are subject to garbage collection (unless other
* references to these copies exist).
大体意思就是ThreadLocal 实现一个线程本地的存储。每个线程都有自己的局部变量。所有线程都共享一个ThreadLocal对象,但是每个线程在访问这些变量的时候能得到不同的值,每个线程可以更改这些变量并且不会影响其他的线程,并且支持null值。
我们看下ThreadLocal的方法有哪些?
- void set(Object value)设置当前线程的线程局部变量的值。
- public Object get()该方法返回当前线程所对应的线程局部变量。
- public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
- protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
我们使用一下ThreadLocal
创建一个ThreadLocal,存入一段文本,看在两个线程取出值是多少。
local = new ThreadLocal<>();
local.set("存入测试");
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
Log.e(TAG, "t1 get(): " + local.get());
local.set("t1");
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
Log.e(TAG, "t2 get() " + local.get());
}
});
t1.start();
t2.start();
Log.e(TAG, "main get(): "+local.get() );
结果
我们发现在t1 t2中取出值都是null,那么我们在t1 t2中存储再取出验证下
local = new ThreadLocal<>();
local.set("存入测试");
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
local.set("t1");
Log.e(TAG, "t1 get(): " + local.get());
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
local.set("t2");
Log.e(TAG, "t2 get() " + local.get());
}
});
t1.start();
t2.start();
Log.e(TAG, "main get(): "+local.get() );
image.png
获取大家好奇,怎么就在拿不到local的值呢,我们上面说到ThreadLocal是每个线程可以访问自己内部的副本变量。
我们看下ThreadLocal是怎么实现这种神奇机制的的?
先看下set方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
我们不去关注细节,大家有时间可以仔细看下源码,我们获取了一个map,进行了判null处理,将值存进去。如果是null就去创建map并将值赋进去。可以理解为key就是当前的线程。
再看一下get()方法。
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();
}
根据线程,取出一个map,并根据类型取出一个Entry,根据泛型强转,并返回,如果没有的话,我们看到一个setInitialValue追一下
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;
}
protected T initialValue() {
return null;
}
可以看到如果没查到值initialValue,会默认返回一个null。
其实ThreadLocalMap的源码大家可以看一下,因为代码量较多,我就不粘出来了。我们刚看到的Entry其实是一个WeakReference。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
最后看下remove()方法
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
其实也就是根据当前线程判断,删除this,这里的this指向的就是ThreadLocal自己。
其实还有很多细节,大家可以研究下源码的时候看一下,想一下
这篇文章写的不错,大家可以看看。
https://www.cnblogs.com/xzwblog/p/7227509.html