JDK里有一个ThreadLocal这么一个类,其实起这个名字不是很贴近,这个类相当于给线程设置上了一个局部变量。使得,不会因为多线程访问同一个资源而产生多线程同步问题。
因为这个ThreadLocal类里面放的是每个线程都拥有一个副本,线程之间彼此不会互相影响。
现在这里笔者将会从源码的实现角度给大家讲述一下ThreadLocal实现的原理。
首先我们来看一下set()这个方法的实现。
所有的讲解笔者都将以注释的形式给出。
public void set(T value) {
//拿到当前线程对象
Thread t = Thread.currentThread();
//拿到一个Map对象,好,那么问题来了?这个是一个什么样的map对象呢??
//其实这个ThreadLocalMap对象是ThreadLocal类内部自己实现的一个类似于HashMap这样一个类
ThreadLocalMap map = getMap(t);
//如果map非空,则将当前的ThreadLocal对象和这个set()方法的参数put到这个Map里面去,
//如果没有将其创建。
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
我们可以看到ThreadLocal这个类里面确实定义了一个ThreadLocalMap类。里面的实现和HashMap的差不多,笔者就不点进去看了。
再一个我们点进去get()方法里面去看以下。
public T get() {
//拿到当前的线程对象
Thread t = Thread.currentThread();
//拿到ThreadLocalMap对象,到了这里我们进去getMap()方法里面去看一下
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的实现。
ThreadLocalMap getMap(Thread t) {
//从这里可以看出 ThreadLocalMap对象是由当前的线程对象持有的,
//但是维护的工作是由ThreadLocal这个类来完成的。
return t.threadLocals;
}
//createMap方法在当前线程对象内部持有的那个ThreadLocalMap对象为空,会调用之
//从这里我们也可以看出是ThreadLocal这个类在维护这个关系
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
写到这里我们可以下一个结论。
ThreadLocal底层是使用一个自己实现的Map来存储用户set()进来的值。 那个map的键,即为当前的ThreadLocal对象。
有的朋友可能会问了。现在ThreadLocal只有一个对象。那么Map里的值还不是会被其他线程所共享。
其实这个不是的,笔者刚开始也是这么考虑的----------->这个Map里存储的应该是当前的Thread对象和值。而不应该是上面那种。
其实不是的。 因为那个ThreadLocal类自己实现的Map对象是每个线程对象内部自己持有一份。所以说,每个线程对象内的ThreadLocalMap对象是不一样的。所以,里面的数据是不会被其他线程所共享,都是自己用自己的。
这里笔者写了一个demo来证明每个线程持有的ThreadLocalMap对象是不同的。
package multiThread;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.CyclicBarrier;
class MyThread implements Runnable
{
private CyclicBarrier cyclicBarrier;
/**
* 第一个线程所持有的ThreadLocalMap对象
*/
static Object object1 = null;
/**
* 第二个线程所持有的ThreadLocalMap对象
*/
static Object object2 = null;
public MyThread(CyclicBarrier cyclicBarrier)
{
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run()
{
ThreadLocal threadLocal = new ThreadLocal();
Thread thread = Thread.currentThread();
Class> clazz = ThreadLocal.class;
Method createMap = null;
try
{
Method[] methods = clazz.getDeclaredMethods();
Method createMapMethod = null;
/**
* 通过反射拿到createMap方法的Method对象
*/
for(Method method : methods)
{
if("createMap".equals( method.getName() ))
{
createMapMethod = method;
}
}
System.out.println( createMapMethod );
if(createMapMethod != null)
{
createMapMethod.setAccessible( true );
createMapMethod.invoke( threadLocal, thread, "helloworld" );
}
/**
* 通过执行getMap()方法拿到那个LocalThreadMap对象
*/
Method getMap = clazz.getDeclaredMethod( "getMap", Thread.class );
getMap.setAccessible( true );
System.out.println( getMap.invoke( threadLocal, thread ) );
if("thread1".equals( Thread.currentThread().getName() ))
{
object1 = getMap.invoke( threadLocal, thread );
}
if("thread2".equals( Thread.currentThread().getName() ))
{
object2 = getMap.invoke( threadLocal, thread );
}
/**
* 等两个线程都先拿到那个ThreadLocalMap对象才开始执行CyclicBarrier构造方法里面的那个Runnable接口的匿名类里的run方法,
* 由最后一个完成的线程执行。
*/
cyclicBarrier.await();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
public class ThreadLocalTest
{
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InterruptedException, NoSuchMethodException, InstantiationException, InvocationTargetException
{
CyclicBarrier cyclicBarrier = new CyclicBarrier( 2, new Runnable()
{
@Override
public void run()
{
/**
* 判断两个线程对象内所持有的ThreadLocalMap是否为同一对象
*/
System.out.println(MyThread.object1 == MyThread.object2);
}
} );
MyThread myThread = new MyThread( cyclicBarrier );
Thread thread1 = new Thread( myThread, "thread1" );
Thread thread2 = new Thread( myThread, "thread2" );
thread1.start();
thread2.start();
}
}