一 什么是ThreadLocal
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程(哪里存的哪里才能取到)才可以获取到存在的数据,说白了,它的作用域就是线程,你现在线程A中存储了值,只能在线程A中取得到。
eg:在thread1中存入数据,只有在thread1中才能取到这个数据,在thread2等其他线程获取不到该数据。
public class Main2Activity extends AppCompatActivity {
private static final String TAG = "Main2Activity";
ThreadLocal<String> threadLocal;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
threadLocal = new ThreadLocal<>();
threadLocal.set("主线程");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
threadLocal.set("存储数据");
Log.e(TAG, "run: " + threadLocal.get());
}
});
thread.start();
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
Log.e(TAG, "run: " + threadLocal.get());
}
});
thread2.start();
Log.e(TAG, "run: " + threadLocal.get());
}
}
打印结果
07-24 17:20:56.294 8091-8091/? E/Main2Activity: run: 主线程
07-24 17:20:56.294 8091-8104/? E/Main2Activity: run: 存储数据
07-24 17:20:56.295 8091-8105/? E/Main2Activity: run: null
通过上面的例子我们发现,明明用的是一个全局的变量threadLocal,但是3条日志内容却不一样。这就是threadLocal精华所在了。
二源码解析
先看set和get方法
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);
}
可以看到,先拿到当前线程,,,,注意是当前线程(就是调用方法时候所在的线程),然后通过该线程拿到ThreadLocalMap 这个对象。。。它是个ThreadLocal的静态内部类。
源码注释是这样的
/**
* 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.
*/
就是本地线程存储数据的一个类,然后用这个对象去set值,key传的是当前线程,value就是内容了。
我们在进一步深入点进去map.set()方法
/**
* Set the value associated with key.
*
* @param key the thread local object
* @param value the value to be set
*/
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
这里涉及到的算法就不管了,如果得到的K是就当前ThreadLocal对象,就赋值。
if (k == key) {
e.value = value;
return;
}
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();
}
这个更明了了,得到当前线程,获取ThreadLocalMap对象,然后在获取ThreadLocalMap.Entry 取值。如果ThreadLocalMap为空则会返回空,setInitialValue()
方法默认返回空的。
总结
ThreadLocal是一个线程本地变量,一个作用域在线程的变量,不同的线程设置和修改的ThreadLocal值互不影响
原理:ThreadLocal在get值和set值的时候都会先获取当前线程的ThreadLocalMap对象,ThreadLocalMap把ThreadLocal作为key来取值或者存值(相当于每个线程都有个ThreadLocal对象),这样对于每一个线程所存储的数据都是基于线程的,互不相干。
网友评论