ThreadLocal
关于ThreadLocal请参考这篇文章 解密ThreadLocal, 基本上都全了.这里主要是写一些自己的理解
ThreadLocal的实现
这里我们从运行时内存中实例分配的角度来分析ThreadLocal的实现,测试代码如下:
public class Main {
public static ThreadLocal<String> sUserName = new ThreadLocal<>();
public static ThreadLocal<Integer> sUserCount = new ThreadLocal<>();
public static void main(String[] args) {
Thread1 t1 = new Thread1();
Thread2 t2 = new Thread2();
t1.start();
t2.start();
}
public static class Thread1 extends Thread {
@Override
public void run() {
// Step 1
sUserCount.set(1);
// Step 2
sUserName.set("A");
}
}
public static class Thread2 extends Thread {
@Override
public void run() {
// Step 3
sUserName.set("B");
}
}
}
代码很简单,有两个ThreadLocal变量 sUserCount 和 sUserName, 两个线程 t1 和 t2 分别对其进行 set.
对Step 1, 在线程 t1 中执行ThreadLocal.set,会通过 t1 找到线程内部的一个 Entry 数组table, 再根据 sUserCount 得到该 ThreadLocal 变量在table中的位置(使用 hashcode 对 table.length 求模),然后添加,Step 2 和 Step 3是同样的过程, 我们可以理解为下面的伪代码:
table1 = t1.threadLocals.table
table2 = t2.threadLocals.table
// step1
index1 = sUserCount.threadLocalHashCode % table1.length
table1[index1] = new Entry(sUserCount, 1);
//step2
index2 = sUserName.threadLocalHashCode % table1.length
table1[index2] = new Entry(sUserName, "A");
//step3
index3 = sUserName.threadLocalHashCode % table2.length
table2[index3] = new Entry(sUserName, "B");
然后当我们调用ThreadLocal.get()时, 例如在 t1中调用 sUserName.get(),就相当于执行:
t1.threadLocals.table[index2].get(sUserName)
最终内存中的情况:
虚线代表WeakReference
ThreadLocal的作用
ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度
例如,我们现在一个线程里有三个函数process1
process2
process3
都需要使用卡的信息
public class UsageAccessThread extends Thread {
@Override
public void run() {
int usedCount = 0;
usedCount++;
process1(usedCount);
usedCount++;
process2(usedCount);
usedCount++;
process3(usedCount);
}
private void process1(int usedCount) {
//do process1
System.out.println("Card " + Thread.currentThread().getName() + " used " + usedCount + " times");
}
private void process2(int usedCount) {
//do process2
System.out.println("Card " + Thread.currentThread().getName() + " used " + usedCount + " times");
}
private void process3(int usedCount) {
//do process3
System.out.println("Card " + Thread.currentThread().getName() + " used " + usedCount + " times");
}
}
这里为了方便只是使用一个int型来代表卡的信息. 这样写的话每个函数都得有参数 usedCount, 可以使用ThreadLocal解决这个问题(也可以把 usedCount 作为 UsageAccessThread 的成员变量来解决, 但是这样代码跟使用函数参数没什么简化)
public class Main {
public static ThreadLocal<Integer> sUsedCount = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
......
}
public class UsageAccessThread extends Thread {
@Override
public void run() {
process1();
process2();
process3();
}
private void process1() {
sUsedCount.set(sUsedCount.get()+1);
//do process1
System.out.println("Card " + Thread.currentThread().getName() + " used " + sUsedCount.get() + " times");
}
private void process2() {
sUsedCount.set(sUsedCount.get()+1);
//do process1
System.out.println("Card " + Thread.currentThread().getName() + " used " + sUsedCount.get() + " times");
}
private void process3() {
sUsedCount.set(sUsedCount.get()+1);
//do process1
System.out.println("Card " + Thread.currentThread().getName() + " used " + sUsedCount.get() + " times");
}
}
这里特意把sUsedCount写到Main函数里,只是为了突出它是static, 即它与UsageAccessThread无关(不是UsageAccessThread的成员变量), 对比前面不使用ThreadLocal的写法,UsageAccessThread更简洁些,或者说几个 process 函数更简洁些。按照我的理解这个就是很多文章里提到的使用ThreadLocal可以简洁地编写出优美的多线程程序?