主要功能:
针对一个对象,每个线程需要存储不同的值的时候,使用ThreadLocal会非常的方便。
图解:
针对一个全局变量local,线程1-4都持有local的引用。当线程分别对local进行修改后,其实修改作用域仅限当前线程可用。
public class ThreadLocalTest {
//对thread1-4均可见的mThreadLocal,相当于上图中的String local变量
private ThreadLocal<String> mThreadLocal = new ThreadLocal<String>() ;
//设定一个锁,好对线程进行同步
private final Object setLock = new Object() ;
Thread t1 = new Thread(){
public void run(){
synchronized (setLock) {
mThreadLocal.set("I'm t1");
try {
setLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.d("gggl" , mThreadLocal.get()) ;
}
} ;
Thread t2 = new Thread(){
public void run(){
synchronized (setLock) {
mThreadLocal.set("I'm t2");
try {
setLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.d("gggl" , mThreadLocal.get()) ;
}
} ;
Thread t3 = new Thread(){
public void run(){
synchronized (setLock) {
mThreadLocal.set("I'm t3");
try {
setLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.d("gggl" , mThreadLocal.get()) ;
}
} ;
Thread t4 = new Thread(){
public void run(){
synchronized (setLock) {
mThreadLocal.set("I'm t4");
try {
setLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.d("gggl" , "t4 " + this.getState()) ;
Log.d("gggl" , mThreadLocal.get()) ;
}
} ;
public void test(){
mThreadLocal.set("I'm main");
t1.start() ;
t2.start() ;
t3.start();
t4.start();
try {
Thread.sleep(500) ;
synchronized (setLock) {
setLock.notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
}
Log.d("gggl" , mThreadLocal.get()) ;
}
}
/**
打印结果
2022-09-15 14:46:09.873 12915-15252/com.example.interview D/gggl: I'm t1
2022-09-15 14:46:09.873 12915-15253/com.example.interview D/gggl: I'm t2
2022-09-15 14:46:09.873 12915-15254/com.example.interview D/gggl: I'm t3
2022-09-15 14:46:09.873 12915-12915/com.example.interview D/gggl: I'm main
2022-09-15 14:46:09.873 12915-15255/com.example.interview D/gggl: t4 RUNNABLE
2022-09-15 14:46:09.873 12915-15255/com.example.interview D/gggl: I'm t4
* */
分析:
首先介绍几个相关类:Thread、ThreadLocal、ThreadLocalMap,这三个类的大致关于如下。
ThreadLocalMap是最后存储数据的类,为ThreadLocal的静态内部类,内部包含一个Entry[] table的数组。Entry的key是ThreadLocal,value为我们调用set方法的时候设置的值。
Thread持有ThreadLocal的引用,这表明每创建一个线程都会对应的创建出一个ThreadLocalMap(注:lazyload,因为并不是每个线程都有使用ThreadLocal的功能)。
ThreadLocal内部有两个常用方法set,get用于数据的存储。当调用set、get方法时第一句都是Thread.currentThread()首先获取当前线程的引用。所以后续的操作确保了作用域只在当前线程。当调用set方法时,最终调用的其实时 map.set(this, value),意为着向当前线程的成员变量MAP中存入了一个键值对,由于map为每个线程的成员变量,所以我们调用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);
}
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 getMap(Thread t) {
return t.threadLocals;
}
Android Looper的线程唯一性
做过Android开发的同学应该都知道在android中只要线程调用了Looper.prepa()后就会在对应的线程创建一个looper,并且每个线程都只能有一个looper对象,每个线程的looper对象都不相同,正是使用了ThreadLocal达到的特性。
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));
}
sThreadLocal是ThreadLocal的静态变量,说明所有的Thread共有sThreadLocal变量。在内存中的存放形式就相当于有一个Map存放了进程内的所有Looper。这个Map的key就是每个线程的ThreadLocal