ThreadLocal用于在一个线程中存入一个变量,这样,在此线程执行到其它地方时,就可以在通过ThreadLocal取出此变量,ThreadLocal在多线程开发中会经常使用到。
一、ThreaLocal的使用:
1、使用ThreaLocal写入一个"data"字符串:
1.1、在类中声明一个类变量:
private ThreadLocal<String> threadLocal = new ThreadLocal<>();
1.2、设置变量:
threadLocal.set("data");
2、使用ThreaLocal获取存入的字符串:
threadLocal.get();
二、示例代码:
下面的示例代码为四个线程执行一个Runnable对象,Runnable为每个线程随机写入"A"或者"B",然后在读取每个线程的数据,使用ThreadLocal,能保证每个线程前后存取数据的一致性:
Runnable:
public class ThreadLocalRunnable implements Runnable {
private ThreadLocal<String> threadLocal = new ThreadLocal<>();
public void run() {
double random = Math.random();
if (random < 0.5) {
threadLocal.set("A");
System.out.println("线程名称为" + Thread.currentThread().getName() + "-----保留了A");
} else {
threadLocal.set("B");
System.out.println("线程名称为" + Thread.currentThread().getName() + "-----保留了B");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程名称为" + Thread.currentThread().getName() + "-----保留的数据为" + threadLocal.get());
}
}
Thread:
public class ThreadLocalTest {
public static void main(String[] args) {
ThreadLocalRunnable localRunnable = new ThreadLocalRunnable();
Thread thread1 = new Thread(localRunnable);
thread1.setName("thread1");
Thread thread2 = new Thread(localRunnable);
thread2.setName("thread2");
Thread thread3 = new Thread(localRunnable);
thread3.setName("thread3");
Thread thread4 = new Thread(localRunnable);
thread4.setName("thread4");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
测试的执行结果:
线程名称为thread1-----保留了A
线程名称为thread2-----保留了B
线程名称为thread3-----保留了B
线程名称为thread4-----保留了A
线程名称为thread2-----保留的数据为B
线程名称为thread4-----保留的数据为A
线程名称为thread1-----保留的数据为A
线程名称为thread3-----保留的数据为B
可以看出,每个线程存取的数据保持了一致,比如thread1与thread4前后都为A,而thread2与thread3前后都为B。三、原理:
在java中,每个线程对应一个Thread对象,而在Thread中,有这样一个变量:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap类,可以看成是以 ThreadLocal对象为key,Object为value的map,所以可能大家想到了,每次使用threadLocal对象去存一个Object时,实际上是以这个threadLocal对象为key,value为对应Object存储到当前线程的ThreadLocalMap中,取出时,在从当前线程的ThreadLocalMap中以threadLocal对象为key取出对应Object。我们可以看看ThreadLocal的set(Object)与get()的源码:
1、set(Object):
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
可以看出,先通过Thread.currentThread()得到当前线程,然后得到当前的map,如果map为空,创建一个新的map,并将key-value直接传入,如果map不为空,直接将key-value传入,注意,key都为自己,即ThreadLocal的对象。
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();
}
get()方法就简单一些了,得到当前线程的map,然后得到以自己为key的对应value。