什么是ThreadLocal:
可以把它理解成一个容器,用于存放线程的局部变量,它为每个线程提供了一个独立的副本
使用场景:
一般来说,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal
ThreadLocal的使用:
API如下:
- public void set(T value):将值放入线程局部变量中
- public T get():从线程局部变量中获取值
- public void remove():从线程局部变量中移除值
- protected T initialValue():返回线程局部变量中的初始值
例子:
(1)没有使用ThreadLocal:
public interface BusBase {
int getPassengerNum();
}
public class Bus implements BusBase{
private static int passengerNum = 0;
public int getPassengerNum() {
passengerNum = passengerNum + 1;
return passengerNum;
}
}
public class TestThread extends Thread {
private BusBase bus;
public TestThread(BusBase bus) {
this.bus = bus;
}
@Override
public void run() {
for (int i = 0; i < 2; i++) {
System.out.println(Thread.currentThread().getName() + " => " + bus.getPassengerNum());
}
}
}
public class WithoutThreadLocalTest {
public static void main(String[] args) {
Bus studentBus = new Bus();
TestThread thread1 = new TestThread(studentBus);
TestThread thread2 = new TestThread(studentBus);
TestThread thread3 = new TestThread(studentBus);
thread1.start();
thread2.start();
thread3.start();
}
}
控制台打印结果如下:
Thread-2 => 3
Thread-0 => 2
Thread-0 => 5
Thread-1 => 1
Thread-1 => 6
Thread-2 => 4
可以看到,所有线程共用了一个passengerNum变量
(2)使用了ThreadLocal:
public class BusWithThreadLocal implements BusBase{
private static ThreadLocal<Integer> passengerNum = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
public int getPassengerNum() {
passengerNum.set(passengerNum.get() + 1);
return passengerNum.get();
}
}
public class ThreadLocalTest {
public static void main(String[] args) {
BusWithThreadLocal studentBus = new BusWithThreadLocal();
TestThread thread1 = new TestThread(studentBus);
TestThread thread2 = new TestThread(studentBus);
TestThread thread3 = new TestThread(studentBus);
thread1.start();
thread2.start();
thread3.start();
}
}
控制台打印结果如下:
Thread-0 => 1
Thread-0 => 2
Thread-1 => 1
Thread-1 => 2
Thread-2 => 1
Thread-2 => 2
从结果可以看到,每个线程中的passengerNum变量的值互不干扰,一下子变得线程安全了
通过源码(基于jdk 1.8)分析原理:
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);
}
set方法中通过getMap()方法获得了当前线程的ThreadLocalMap对象,getMap方法具体如下:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
可以看到,getMap方法就是返回了当前线程t的threadLocals变量,而threadLocals变量是ThreadLocalMap的一个对象,那么ThreadLocalMap又是啥,源码如下:
/**
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread. To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.
*/
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLocalMap是ThreadLocal的一个静态内部类,从注释可以看出它是用来保存线程本地变量的一个hashMap,它的key是ThreadLocal变量,value即为对应的保存值。
看到这里该明白了吧,每个threadlocal通过set设置的值,最后是保存在了调用时线程的一个hashMap里面,这个hashMap通过threadlocal对象作为key来标识保存的值
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();
}
可以看到,get方法正是从当前线程t的threadLocals变量(ThreadLocalMap)中,通过ThreadLocal对象索引,得到了之前保存下来的value
网友评论