public class Looper {
static final ThreadLocal sThreadLocal = new ThreadLocal();
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));
}
…
}
prepare()方法中调用了ThreadLocal的get和set方法,然而整个过程没有添加同步锁,Looper是如何实现线程安全的?
2.百度转载的分析:
新创建的线程里进行 Looper.prepare() 操作,因为 Looper 的构造方法是私有的
所以要想创建 Looper 对象,还需使用 prepare() 方法创建
而 prepare() 方法的实质则是:
创建 Looper 对象将创建好的 Looper 对象存储到 ThreadLocal 对象里注意 : ThreadLocal 对象在创建 Looper 的时候创建的。
注意看 ThreadLocal 存储 Looper 的处理方式,是保证 ThreadLocal 里没有其他的 Looper,只允许有一份 Looper。
这就是为什么同一个 Thread 线程里只能有一个 Looper 对象的原因。
这就解答了题目的问题。
接下来,我们来看一下为什么 Looper 要由 ThreadLocal 保管呢?
这是 ThreadLocal 源码中对其的介绍,翻译过来就是 ThreadLocal 提供了针对于单独的线程的局部变量,并能够使用 set()、get() 方法对这些变量进行设置和获取,并且能够保证这些变量与其他线程相隔离。
换句话说,通过使用 threadLocal 存储对象,线程和线程之间的彼此的数据就会隔离起来,从而保证了彼此线程的数据安全和独立性。
我们来看一下 ThreadLocal 的 get 方法:
![](https://img-blog.csdnimg.cn/20201211012701940.jpeg?x-oss-process=image
/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2F1Z2Z1bg==,size_16,color_FFFFFF,t_70)
由上图我们看到,get 方法是通过当前的线程thread,取到一个 ThreadLocalMap 对象(这个就把他当做 map 集合对待即可)。
如果得到的map 是空,则进行这个 map 的初始化:
setInitialValue()。
而初始化的方式也很简单,就是创建一个以 ThreadLocal 自身为 key,存入的对象为 value 的 ThreadLocalMap 对象,然后把这个 ThreadLocalMap 存到线程 thread 里面(方便与线程进行绑定)。
如果得到的 map 不为空
则从 ThreadLocalMap 中获取对应的存储对象,如果没有向 ThreadLocal 里调用 set() 方法,这个时候调用 get() 方法返回的值就是 null。
回想一下 Looper 的 prepare() 方法
就很符合这个逻辑。
prepare() 方法会判断 threadLocal 的 get() 方法,如果返回值不为 null,说明调用过了 threadLocal 的set() 方法了。
此时 set 方法其实也无需再看了,无非就是将需要存储的对象当做value,当前的 threadLocal 的引用当做key,存储进 threadLocalMap 里面,然后将其指向线程thread。
所以总述一下,Looper 是通过利用 ThreadLocal 的数据隔离性将 Looper 对象存储在对应的线程中,保证每一个线程只能创建一个 Looper 对象。
参考地址:https://baijiahao.baidu.com/s?id=1672811852535456858&wfr=spider&for=pc
ThreadLocal位于java.lang包中,以下是JDK文档中对该类的描述
Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the same ThreadLocal object, but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. The implementation supports null values.
大致意思是,ThreadLocal实现了线程本地存储。所有线程共享同一个ThreadLocal对象,但不同线程仅能访问与其线程相关联的值,一个线程修改ThreadLocal对象对其他线程没有影响。
ThreadLocal为编写多线程并发程序提供了一个新的思路。如下图所示,我们可以将ThreadLocal理解为一块存储区,将这一大块存储区分割为多块小的存储区,每一个线程拥有一块属于自己的存储区,那么对自己的存储区操作就不会影响其他线程。对于ThreadLocal,则每一小块存储区中就保存了与特定线程关联的Looper。
3.1 Thread、ThreadLocal和Values的关系
Thread的成员变量localValues代表了线程特定变量,类型为ThreadLocal.Values。由于线程特定变量可能会有多个,并且类型不确定,所以ThreadLocal.Values有一个table成员变量,类型为Object数组。这个localValues可以理解为二维存储区中与特定线程相关的一列。
ThreadLocal类则相当于一个代理,真正操作线程特定存储区table的是其内部类Values。
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
Values values(Thread current) {
return current.localValues;
}
既然与特定线程相关,所以先获取当前线程,然后获取当前线程特定存储,即Thread中的localValues,若localValues为空,则创建一个,最后将value存入values中。
void put(ThreadLocal<?> key, Object value) {
cleanUp();
// Keep track of first tombstone. That’s where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;