【概念
ThreadLocal类用来存放线程的局部变量,每个线程都有自己的局部变量彼此之间不共享。TheadLocal<T>主要有以下三个方法:
- public T get():返回当前线程的局部变量。
- protected T initValue():返回当前线程的局部变量初始值。默认情况下 initValue(), 返回 null 。线程在没有调用 set 之前,第一次调用 get 的时候, get 方法会默认去调用 initValue 这个方法。所以如果没有覆写这个方法,可能导致 get 返回的是 null 。当然如果调用过 set 就不会有这种情况了。
- public void set(T value):设置当前线程的局部变量。
ThreadLocal是如何做到为每一个线程提供单独的局部变量呢?实际上在ThreadLocal类中有一个Map缓存,用于存储每一个线程的局部变量。Map中元素的键为线程对象,而值对应线程的变量副本。
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
【工作流程
1.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);
}
我们可以看见ThreadLocalMap是从currentThread中获取的,也就是说这些局部变量真正存储在currentThread中:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
2.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();
}
从currentThread中获取到了ThreadLocalMap,然后再从中获取value。如果没有值,则调用setInitialValue()。
【如何使用
package ThreadLocal;
import java.util.Random;
import static ThreadLocal.ThreadLocalTest.localSetter;
/**
* 在每个线程中单独存放一个值然后再获取
*/
public class ThreadLocalTest {
static ThreadLocal localSetter = new ThreadLocal();
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyThread t = new MyThread();
t.start();
}
}
}
class MyThread extends Thread{
private static Random random = new Random();
@Override
public void run(){
//现在当前线程中存入一个随机变量 v1
int randomVar = random.nextInt(100);
localSetter.set(randomVar);
System.out.println(getName() + " random is " + randomVar);
//获取当前线程的随机变量 v2
//v1 永远等于 v2
System.out.println(getName() + " localVar is " + localSetter.get());
}
}
【spring与ThreadLocal
spring中的bean大多都是单例的,但是我们应用又是支持并发的,所以将各个service,dao存放在ThreadLocal中是一个明智的做法。但是需要注意如果bean中包含共享变量,spring是没有做任何的安全处理的。
【synchronized与ThreadLocal
ThreadLocal以空间换取时间,提供了一种非常简便的多线程实现方式。
ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别:
synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本。Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。