java ThreadLocal与其应用(netty)

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值