写在本文之前:
多线程的初衷是通过使用多个Thread来充分利用CPU资源,并发的完成任务。
多线程的难点就在于如何处理多个线程共享同一资源这一问题。(显而易见不是吗? 如果多个线程互不相干,还有什么需要你费神呢)
因此,concurrency包下的大部分内容、或者说我们所关心的并发问题,一定是应用在这种场景之下的:从宏观上共享的访问同一资源。
即如下图所示,
这里留一个问题先:
thread1、thread2如何拿到Main thread中source的引用?
你可能会说,在创建thread1/2的时候通过参数传引用。这是个办法;
However,如果thread1中方法调用很深,最底层的方法需要操作source,难道我需要在invocation chain中的每一层传递source的引用?
说到这里,你应该猜到了,ThreadLocal的用途之一:可以用来避免在thread中传递参数。
作用:
ThreadLocal又叫线程局部变量。
如果一个变量声明为ThreadLocal类型, 那么每一个与之关联(经过此处)的Thread 都被允许持有一份(当前thread独享的)该ThreadLocal变量的value。
通俗点讲,对于ThreadLocal类型的变量,每一个线程都有一个属于他自己的value,线程之间互不影响。
从效果上来看,ThreadLocal实现的效果和Synchronized实现的效果(多个线程共享同一变量)正好相反。
应用场景:
1. 避免线程内部invocation chain中的参数传递(参见文章开始的问题)。
2. 在线程内部持有对象引用;这个线程内部的对象引用对其他线程是不可见的,并且不同的线程持有的对象名称相同(e.g. 在Web应用的server side, 管理所有session的场景中,就可以使用ThreadLocal将session按照用户登录Thread存储起来)
API:
ThreadLocal.get()
ThreadLocal.set(T t)
ThreadLcoal.initialValue()
e.g.
public class DemoThread extends Thread{
private static String defaultUser = "defaultUser";
private static String defaultPwd = "defaultPwd";
private static ThreadLocal<User> threadUser = new ThreadLocal<>(){
@Override
protected User initialValue(){
User defaultUser = new User(defaultUser, defaultPwd);
return defaultUser;
}
};
...
public void run(){
if(useDefault){
User user = new User(userName, pwd);
threadUser.set(user);
}
...
User currentUser = threadUser.get();
}
}
JDK实现:
Thread类中有一个属性 threadLocals, 包含了当前Thread所有ThreadLocal变量的值。
public class Thread implements Runnable{
....
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
....
}
以ThreadLocal静态对象为map key,当前Thread关于此ThreadLocal的值为map的value。
public class ThreadLocal<T>{
...
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = t.threadLocals;
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
}