刚开始看netty源代码的时候就遇到了ThreadLocal这个东西,当时就不太明白这个东西到底干嘛用的,上网稍微查了一下,好像大家说法都不太一样,有的说是为了方便线程维护自己的变量,有的又说是什么方便并发编程。。我擦。。感觉凌乱了。。还是自己好好看看靠谱。。。
首先ThreadLocal确实是用于线程存储本地变量的一种很不错的方法,在这里,一般情况下,都用于保存当前变量独有的对象实例,如果好多线程都只是保存同一个对象的引用的话,那么就必须加上额外的同步机制了。。所以说这里和并发一毛线关系都没有。。。。
我们还是来看它在netty中是怎么用的吧,在SingleThreadEventExecutor中定义如下:
static final ThreadLocal<SingleThreadEventExecutor> CURRENT_EVENT_LOOP =
new ThreadLocal<SingleThreadEventExecutor>(); //线程局部变量
这里需要注意的是
static
。。。。
再来看看如何得到存储的变量呢?
public static SingleThreadEventExecutor currentEventLoop() {
return CURRENT_EVENT_LOOP.get(); //返回当前
}
这里也是
static
的。。。
好,接下来我们来看netty是怎么存储线程的本地变量的:
protected SingleThreadEventExecutor(
EventExecutorGroup parent, ThreadFactory threadFactory, boolean addTaskWakesUp) {
if (threadFactory == null) {
throw new NullPointerException("threadFactory");
}
this.parent = parent;
this.addTaskWakesUp = addTaskWakesUp; //唤醒任务
thread = threadFactory.newThread(new Runnable() { //创建线程
@Override //线程的执行函数
public void run() {
CURRENT_EVENT_LOOP.set(SingleThreadEventExecutor.this); //存储当前线程的本地对象
boolean success = false;
updateLastExecutionTime();
try {
SingleThreadEventExecutor.this.run(); //开始当前executor的执行函数,run方法延后到了后面的类中实现
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
if (state < ST_SHUTTING_DOWN) {
state = ST_SHUTTING_DOWN;
}
// Check if confirmShutdown() was called at the end of the loop.
if (success && gracefulShutdownStartTime == 0) {
logger.error(
"Buggy " + EventExecutor.class.getSimpleName() + " implementation; " +
SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must be called " +
"before run() implementation terminates.");
}
try {
// Run all remaining tasks and shutdown hooks.
for (;;) {
if (confirmShutdown()) {
break;
}
}
} finally {
try {
cleanup();
} finally {
synchronized (stateLock) {
state = ST_TERMINATED;
}
threadLock.release();
if (!taskQueue.isEmpty()) {
logger.warn(
"An event executor terminated with " +
"non-empty task queue (" + taskQueue.size() + ')');
}
}
}
}
}
});
taskQueue = newTaskQueue(); //构造任务队列
}
这里需要注意的是,是在线程的run方法里面调用set方法来保存本地变量的。。
要搞清楚究竟是怎么回事,感觉还是看源码最靠谱。。。。
首先我们来看ThreadLocal构造函数吧:
/**
* Creates a thread local variable.
*/
public ThreadLocal() {
}
空方法,白看了。。。
好吧接下来来看看set方法吧:
public void set(T value) {
Thread t = Thread.currentThread(); //当前的运行线程
ThreadLocalMap map = getMap(t); //额,就当是获取一个map吧
if (map != null)
map.set(this, value); //标准的map的set方法,key就是当前的ThreadLocal变量
else
createMap(t, value); //创建map,并保存
}
这里居然出现了一个map,而且这里保存value的时候用的key就是当前的ThreadLocal变量。。。
还是来看看getMap方法,看看map究竟从哪里搞出来的吧。。。
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
这里传进的t就是当前正在执行的线程,可以看出这个map其实就是当前的执行线程的一个属性。。。
嗯,其实看到这里,基本上就明白的差不多了。。。。不过还是来看看get方法吧:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); //跟上面的套路差不多吧,获取map
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); //用当前的ThreadLocal为key,获取value
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
嗯,这个还是比较简单的吧。。。一看就差不多明白什么意思了。。。
好了,ThreadLocal基本的一些概念算是搞清楚了。。。
(1)每一个Thread变量本身就保存有一个map对象(姑且把它理解为map吧)
(2)当set的时候,实际上是将当前的value放入到当前执行线程的那个map当中去,而且这里的key就是当前的ThreadLocal对象。。。
好了,其实这里也能想到ThreadLocal的用法了,在netty中的代码,访问的方法都是static(上面我故意表黑,变大),说明这是为了方便线程能够访问这些变量,而不用将这些变量在对象中传来传去的。。方便的多了。。。可以少写很多的代码。。。