1. ThreadLocal是什么?
早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。其实,ThreadLocal是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
从线程的角度看,ThreadLocal就像是线程的本地变量,这也是类名中“Local”所要表达的意思。
2. ThreadLocal的用法
public class Test {
public static void main(String[] args) {
ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
protected Integer initialValue() {
return 0;
}
};
for(int i = 0 ; i < 5 ; i++){
new Thread(()->{
int i1 = threadLocal.get().intValue()+5;
System.out.println(Thread.currentThread().getName()+"->"+i1);
},"Thread"+i).start();
}
}
}
结果,没个线程都有将自己独立的变量的副本。
Thread0->5
Thread4->5
Thread3->5
Thread2->5
Thread1->5
另外还要一个列子需要注意
public class ThreadLocaTest {
private static Index index = new Index();
static class Index{
int num;
public void incr(){
num++;
}
}
public static void main(String[] args) {
ThreadLocal<Index> threadLocal = new ThreadLocal<Index>(){
protected Index initialValue() {
return index;
}
};
for(int i = 0 ; i < 5 ; i++){
new Thread(()->{
Index index = threadLocal.get();
index.incr();
System.out.println(Thread.currentThread().getName()+"->"+threadLocal.get().num);
},"Thread"+i).start();
}
}
}
输出结果为:
Thread0->1
Thread2->3
Thread1->2
Thread4->5
Thread3->4
我们发现ThreadLocal并没有像预期的那样:每个线程拥有一个单独的局部变量。为什么呢?之前传递Integer是值传递,但是这次传递的是Index的引用。引用指向的是同一个地址。所以改变一个会对原来的对象也进行改变。如果要实现每个线程都有自己的局部变量,那么这么写。直接new 就好了
public class ThreadLocaTest {
private static Index index = new Index();
static class Index{
int num;
public void incr(){
num++;
}
}
public static void main(String[] args) {
ThreadLocal<Index> threadLocal = new ThreadLocal<Index>(){
protected Index initialValue() {
return new Index();
}
};
for(int i = 0 ; i < 5 ; i++){
new Thread(()->{
Index index = threadLocal.get();
index.incr();
System.out.println(Thread.currentThread().getName()+"->"+threadLocal.get().num);
},"Thread"+i).start();
}
}
}
结果是:
Thread0->1
Thread3->1
Thread2->1
Thread1->1
Thread4->1
3. ThreadLocal的原理
首先看ThreadLocal.get() 方法
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();
}
getMap(t)
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
可以看到,返回的是Thread类中的一个ThreadLocalMap,就是说,每个线程都有一个ThreadLocalMap
接着往下看,如果ThreadLocalMap 不等于null就直接取值,如果为Null就SetInitialValue(),
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();
}
private T setInitialValue() {
// 我们当初在新建ThreadLocal的时候重写了InitialValue() 方法,所以这里调用的是我们重写的方法
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
// 如果map为null,那么就新建一个Map
createMap(t, value);
return value;
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//其实就是新建了一个Entry的数组
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}