復習和回顧Android知識蘸泻,梳理筆記
ThreadLocal簡介
ThreadLocal一般在開發(fā)中不是很常見诵棵,但是了解過Android消息機制的應該都看過Handler及Looper的源碼尘应,在Looper源碼里有出現過:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
ThreadLocal源碼給出的定義(英文翻譯的有點渣):一個為線程本地提供數據的類箕肃,這些數據從每個線程獲取到的都是不同的茉帅,每個線程獨立初始化這些數據的副本齐佳。 簡單來說就是ThreadLocal為不同線程存儲數據,并且每個線程存儲的數據是獨立的填硕,修改互不影響其他線程麦萤。
舉個栗子
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import player.video.wzh.com.videoplayer.R;
public class ThreadLocalAct extends FragmentActivity {
public String TAG = "wenzhihao";
/**
* 定義ThreadLocal
*/
private ThreadLocal<Student> threadLocal = new ThreadLocal(){
@Override
protected Student initialValue() {
return new Student("NULL");
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_threadlocal);
/**
* 在主線程設置一個對象
*/
threadLocal.set(new Student("張三"));
initTest();
}
private void initTest() {
Log.e(TAG,Thread.currentThread().getName()+"-->"+threadLocal.get().toString());
/**
* 開啟一個子線程獲取Student
*/
new Thread(){
@Override
public void run() {
super.run();
Log.e(TAG,Thread.currentThread().getName()+"-->"+threadLocal.get().toString());
}
}.start();
/**
* 開啟一個子線程再set一個Student
*/
new Thread(){
@Override
public void run() {
super.run();
threadLocal.set(new Student("麗麗"));
Log.e(TAG,Thread.currentThread().getName()+"-->"+threadLocal.get().toString());
}
}.start();
Log.e(TAG,Thread.currentThread().getName()+"-->"+threadLocal.get().toString());
}
public class Student{
public Student(String name) {
this.name = name;
}
public String name ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
}
運行結果(結果具有隨機性鹿鳖,線程非順序執(zhí)行):
E/wenzhihao: main---------Student{name='張三'}
E/wenzhihao: Thread-1200---------Student{name='NULL'}
E/wenzhihao: main---------Student{name='張三'}
E/wenzhihao: Thread-1201---------Student{name='麗麗'}
這這個栗子首先創(chuàng)建一個ThreadLocal,并重新了initialValue方法壮莹,如果不重寫此方法翅帜,上來就get會報錯,等于給它初始化命满。在主線程去set一個Student對象涝滴,然后開啟2個子線程,一個線程去直接從ThreadLocal取數據胶台,一個線程先設置另一個Student對象再去取數據歼疮,最后在主線程打印取出的數據。
結果證明在一個線程存儲數據只能在當前線程有效诈唬,并不影響其他線程的數據韩脏。
源碼分析
ThreadLocal<T>存儲的是數據,創(chuàng)建的時候需要為其指定泛型铸磅,最核心的方法應該就是set和get了赡矢,set是往當前線程保存數據,get是從當前線程獲取數據阅仔。
T 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 是一個內部類吹散,內部有一個數組,數組里面存儲的是Entry霎槐,該類繼承自弱引用,
//內部是以鍵值對形式存儲數據送浊,鍵就是ThreadlLocal,值就是要存儲的對象丘跌,每個線程都定義有這么一個對象
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;
}
}
//取出當前線程保存的數據返回袭景,否則就創(chuàng)建初始值
//setInitialValue()內部調用initialValue() ,這里看出我們?yōu)槭裁匆趧?chuàng)建時候重寫initialValue()方法了闭树,不重寫默認返回null
return setInitialValue();
}
ThreadLocalMap getMap(Thread t) ——獲取該線程的ThreadLocalMap 對象
/**
* 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;
}
void set(T value) ——為該線程存儲數據
/**
* 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耸棒,把數據對象存到這個數據結構中
void remove()——移除當前線程ThreadLocal存儲的數據
/**
* 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
*/
//獲取當前線程的ThreadLocalMap,把數據對象從改線程移除掉
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
通過源碼分析可以看出报辱,其實ThreadLocal在每個線程的數據操作与殃,都是在操作每個線程的ThreadLocalMap ,每個線程都有一個ThreadLocalMap 碍现,每個ThreadLocalMap 都有一個table數組存儲著數據幅疼,set和get操作其實就是在這個table數組進行數據的操作(table數組存儲的對象是Entry虛引用以鍵值對存儲數據,鍵——ThreadLocalMap 自身昼接,值——存儲的對象)爽篷,所以ThreadLocal可以在多個線程中互不干擾的操作自己的數據。