在学习Handler机制的过程中, 了解到Looper的成员属性消息队列MessageQueue是通过ThreadLocal实现了每个线程只有一个Looper对象, 每个Looper对象只有一个MessageQueue对象.
实际上, 通常我们把ThreadLocal作为线程隔离的工具使用.
没接触过Android的Handler机制的朋友, 下面讲述的内容不会影响你理解ThreadLocal源码.
分析源码要有的放矢, 带着问题去分析ThreadLocal源码:
首先提出问题: 为什么ThreadLocal可以作为线程隔离的工具?
问题提出后, 开始分析ThreadLocal, 列出常用的两个成员方法:
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)
return (T)e.value;
}
return setInitialValue();
}
分析以上两个方法:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
/**
* 此方法其实就是浓缩了HashMap精髓的方法
*/
private void set(ThreadLocal key, Object value) {
Entry[] tab = table;
int len = tab.length;
//这里出现了threadLocalHashCode
int i = key.threadLocalHashCode & (len-1);//计算数组下标
//如果在数组table中以下标为i的元素的链表中能找到(方法传进来的)以key为成员属性的Entry对象, 则把该Entry对象的value成员属性修改为(方法传进来的)value, 并return返回本方法
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;
}
}
//如果for循环中没有找以key为成员属性的Entry对象, 则新创建一个Entry对象, 并放到table数组中
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
//threadLocalHashCode又出现了
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//ThreadLocal对象作为key, firstValue作为value保存在Entry中
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
可以看出执行了createMap(Thread t, T firstValue)后, 当前Thread对应的ThreadLocalMap对象threadLocals不再为null.
在这个方法中也可以清楚的看出, ThreadLocalMap是以ThreadLocal对象作为key保存传进来的值.
值得注意的是, 此方法如果在同一个线程中调用多次, 后面的对象会替换前面设置的对象.
/**
* 自定义Looper
*/
public class Looper {
/**
* 静态
*/
final static ThreadLocal<Looper> testThreadLocal = new ThreadLocal<>();
/**
* 不允许new
*/
private Looper() {
}
/**
* 通过此方法创建本类实例
*/
public static void prepare() {
//在set之前先get
if (testThreadLocal.get() != null) {
throw new RuntimeException("每个线程只能调用一次prepare()方法");
}
testThreadLocal.set(new Looper());
}
/**
* 通过此方法获取本类实例
*
* @return
*/
public static Looper myLooper() {
return testThreadLocal.get();
}
}
以上设计可以保证一个Thread对应一个ThreadLocal, 也保证了一个Thread只对应某个类的实例.
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode = new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
public final int getAndAdd(int delta) {
for (;;) {
//获取原来的值, 最初默认值为0(重点)
int current = get();//获取AtomicInteger#value
//设置期望值
int next = current + delta;
//如果current == AtomicInteger#value, 则赋值AtomicInteger#value = next
if (compareAndSet(current, next))
return current;//返回原AtoomicInteger#value值
}
}
方法分析:
getAndAdd(int delta)最后会调用compareAndset()方法:
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
最终会调用Unsafe类的compareAndSwapInt()方法, 通过原子操作实现了CAS(Compare And Swap)操作,compareAndSwapInt()方法跟踪到最后, 最底层基于汇编语言实现。 跟系统(CPU指令集)挂钩, 这里就不深究了.
编程中, 设定"原子"代表最小的单位,所以原子操作可以看做最小的执行单位,该操作在执行完毕前不会被任何其他任务或事件打断。
AtomicInteger类相关测试:
/**
* Created by tgvincent on 2019/7/8.
* 并发编程, 测试{@link java.util.concurrent.atomic.AtomicInteger}
*/
public class AtomicIntegerTest {
//volatile让num对线程可见(即变化可被线程感知)
private volatile int num = 10;
public static void main(String[] args) {
AtomicIntegerTest test = new AtomicIntegerTest();
int temp = test.num;
System.out.println("test.num: " + test.num);
System.out.println("temp: " + temp);
AtomicInteger atomicInteger = new AtomicInteger(test.num);
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
test.num++;
}
}).start();
try {
Thread.sleep(3000);//休眠3秒确保上述线程执行完毕
System.out.println("=================After thread execution=================");
System.out.println("test.num: " + test.num);
System.out.println("temp: " + temp);
boolean cas = atomicInteger.compareAndSet(test.num, 999);
int result = atomicInteger.get();
/**
* test.num 已经变为1010, != 10, 所以, {@link AtomicInteger#compareAndSet(int, int)}
* 不会发生交换, 所以{@link AtomicInteger#get()} 还是原来的值10
*/
System.out.println("is swap?: " + cas +", result: "+ result);
/**
* temp为10, == 10, 所以, {@link AtomicInteger#compareAndSet(int, int)}
* 发生交换, 交换之后, {@link AtomicInteger#value}为999,
* 所以{@link AtomicInteger#get()} 获取的值为999
*/
cas = atomicInteger.compareAndSet(temp, 999);
result = atomicInteger.get();
System.out.println("is swap?: " + cas +", result: "+ result);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试结果:
test.num: 10
temp: 10
=================After thread execution=================
test.num: 1010
temp: 10
is swap?: false, result: 10
is swap?: true, result: 999
补充问题: 为什么是0x61c88647? 以下为网上搜索的资料: