java ThreadLocal与其应用(netty)

来源:互联网 发布:淘宝售假违规扣24分 编辑:程序博客网 时间:2024/05/22 10:37

刚开始看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(上面我故意表黑,变大),说明这是为了方便线程能够访问这些变量,而不用将这些变量在对象中传来传去的。。方便的多了。。。可以少写很多的代码。。。

原创粉丝点击