ThreadLocal可以实现线程本地存储的功能。把共享数据的可见范围限制在同一个线程内,就无须同步也能保证线程间不出现数据争用的问题。 那么它是如何实现解决数据争用的问题呢。看代码
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();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
看代码就知道,通过 currentThread 获取 ThreadLocalMap。也就是说每个 Thread 都保存着 ThreadLocalMap。 接下来看看 ThreadLocalMap。它有个内部类 Entry。很明显,key就是ThreadLocal,value就是要保存的值。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLocalMap 内部就保存着这样的数组。同时注意是 WeakReference,不一定要等到 OOM 才会去回收 看一下 ThreadLocalMap 的构造函数,注意 table[i] 的算法(除留余数法?)
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
再看一下resize
private void resize() {
Entry[] oldTab = table;
int oldLen = oldTab.length;
int newLen = oldLen * 2;//很明显 扩容成原来的两倍
Entry[] newTab = new Entry[newLen];
int count = 0;
for (int j = 0; j < oldLen; ++j) {
Entry e = oldTab[j];
if (e != null) {
ThreadLocal<?> k = e.get();
if (k == null) {
e.value = null; // Help the GC
} else {
int h = k.threadLocalHashCode & (newLen - 1);//重新计算下标
while (newTab[h] != null)
h = nextIndex(h, newLen);
newTab[h] = e;
count++;
}
}
}
setThreshold(newLen);
size = count;
table = newTab;
}
由此可见,ThreadLocal 就是为每个线程创建了一个数组table(key是ThreadLocal对应的hash值,val就是值) set的时候先根据Thread找到对应的table,然后在根据hash算法找到下标index,最终找到值。get同理
很典型的就是常见的Handler和Looper
public final class Looper {
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
//通常我们会在子线程 Looper.prepare() .主线程早在创建的时候就已经有了
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));
}
public static void loop() {
final Looper me = myLooper();//这里获取对应的loop。如果是子线程的looper,那么handleMsg最终一定在子线程
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
}
}
public static Looper myLooper() {
return sThreadLocal.get();
}
}
那么,接下来这段代码就很清晰了
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
// Handler 构造函数如果没有指明 Looper.getMainLooper(),那么默认是子线程的 Loop,下面的 is main thread 打印出来是 false
handler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Log.e("handler", "is main thread = " + isMainThread() + ", what = " + msg.what);
return false;
}
});
handler.sendMessageDelayed(Message.obtain(handler, 1), 2000);
Looper.loop();
}
}).start();
findViewById(R.id.text).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handler.sendMessageDelayed(Message.obtain(handler, 2), 3000);
}
});