ThreadLocal工作原理

已经有几个月没有写博客了,虽然作为一名菜鸟,但是学习还是要持之以恒的,多写写博客,总结一下自己的学习成果还是不错的。今天,我们就一起来学习一下ThreadLocal吧!

说起ThreadLocal,应该大家都觉得有点陌生。确实,在日常的开发中,ThreadLocal用的地方比较少,但是在某些场景下,它是可以完成复杂的功能。例如Android消息机制,Handler和Looper。Handler工作的时候,需要获取Looper,但是Looper的作用域是线程,而且不同的线程具有不同的Looper。这个时候,使用ThreadLocal就比较方便了,因为它是一个线程内部的存储类,它可以在指定线程中存储数据,而对于其他线程来说则是无法获取的。我们来看看一个例子:

private ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("000000");
        System.out.println("Thread#main ThreadLocal=" + threadLocal.get());

        new Thread("Thread#1"){
            @Override
            public void run() {
                threadLocal.set("111111");
                System.out.println("Thread#2ThreadLocal=" + threadLocal.get());
            }
        }.start();

        new Thread("Thread#2"){
            @Override
            public void run() {
                threadLocal.set("222222");
                System.out.println("Thread#2ThreadLocal=" + threadLocal.get());
            }
        }.start();

在上面的例子中,算上主线程,一共开启的三个线程,在每个线程里面都有一个共同的ThreadLocal,而且也进行了数据的存储和获取,看看结果是怎样的:

这里写图片描述

每个线程之间的ThreadLocal值没有互相影响,这是为什么呢?我们从源码上去解析。

ThreadLocal是一个泛型类,他的定义是:

public class ThreadLocal<T>

我们首先看看它的set()方法:

public void set(T value) {
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values == null) {
        values = initializeValues(currentThread);
    }
    values.put(this, value);
}

currentThread获取的是当前线程的实例,接着出现了一个Values的类,其实这个就是ThreadLocl的静态内部类static class Values,LocalThread.Values。
接着就会调用values()方法,这个方法究竟是做干什么的?我们看看它的代码:

Values values(Thread current) {
        return current.localValues;
    }

原来values()方法就是获取当前线程的ThreadLocal数据,因为在Thread类的内部,有一个成员专门用于存储线程的ThreadLocal的数据:

ThreadLocal.Values localValues;

恰好就是ThreadLocal的静态内部类Values, 通常这个值默认是为null的,所以在一开始的时候会进行初始化工作initializeValues()。
那么如何将ThreadLocal的值进行存储,就要看看values.put()方法了。

void put(ThreadLocal<?> key, Object value) {
    cleanUp();
    // Keep track of first tombstone. That's where we want to go back
    // and add an entry if necessary.
    int firstTombstone = -1;
    for (int index = key.hash & mask;; index = next(index)) {
        Object k = table[index];
        if (k == key.reference) {
            // Replace existing entry.
            table[index + 1] = value;
            return;
        }
        if (k == null) {
            if (firstTombstone == -1) {
                // Fill in null slot.
                table[index] = key.reference;
                table[index + 1] = value;
                size++;
                return;
            }
            // Go back and replace first tombstone.
            table[firstTombstone] = key.reference;
            table[firstTombstone + 1] = value;
            tombstones--;
            size++;
            return;
        }
        // Remember first tombstone.
        if (firstTombstone == -1 && k == TOMBSTONE) {
            firstTombstone = index;
        }
    }
}

这里面我们算法不用去管,但是我们能看出的就是ThreadLocal的值是存储在table[]数组中,而且是存储在ThreadLocal的reference字段所标识的下一个位置。假如ThreadLocal的reference在table[]中的下标是index,那么ThreadLocal的值就是存储在table[index+1]=value。

现在再分析它的get()方法:

public T get() {
    // Optimized for the fast path.
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values != null) {
        Object[] table = values.table;
        int index = hash & values.mask;
        if (this.reference == table[index]) {
            return (T) table[index + 1];
        }
    } else {
        values = initializeValues(currentThread);
    }
    return (T) values.getAfterMiss(this);
}

get就是比较清晰的,获取当前线程的ThreadLocal.Values对象,再获取values里面的table[]数组的table[index+1]的值。

总结:

ThreadLocal的存储原理,其实就是获取当前线程的localValues对象中的table[]数组,不同的线程,localValues是不同的,所以他就能在多线程中互不干扰地存储数据和修改数据。

猜你喜欢

转载自blog.csdn.net/lihuanxing/article/details/55506850
今日推荐