android ThreadLocal的原理解析和用法

来源:互联网 发布:在ubuntu运行apk软件 编辑:程序博客网 时间:2024/05/08 23:44

ThreadLocal是一个存储数据的一个类,主要用在不同线程数据的存储,每个线程都有自己的标志,线程间的数据是用共同的ThreadLocal的对象,但是存储数据值是不同的。下面通过一个小例子来了解用法。
在一个Activity内分别在2个子线程和主线程上分别存数据和取数据。

public class MainActivity extends AppCompatActivity {    private  ThreadLocal<String> mThreadLocal;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mThreadLocal = new ThreadLocal<>();        mThreadLocal.set("主线程");        Log.i("zhang------------------","threadMain值_"+mThreadLocal.get());        new Thread("ThreadOne"){            @Override            public void run() {                super.run();                mThreadLocal.set("线程一");                Log.i("zhang------------------","threadOne值_"+mThreadLocal.get());            }        }.start();        new Thread("ThreadTwo"){            @Override            public void run() {                super.run();                mThreadLocal.set("线程二");                Log.i("zhang------------------","threadTwo值_"+mThreadLocal.get());            }        }.start();    }}

运行结果如下:

03-19 16:15:40.533 6817-6817/? I/zhang------------------: threadMain值_主线程03-19 16:15:40.538 6817-6834/? I/zhang------------------: threadOne值_线程一03-19 16:15:40.542 6817-6835/? I/zhang------------------: threadTwo值_线程二

从运行结果可以看出不同线程访问的是同一ThreadLocal对象,但是在对应的线程内获取到的值是不一样的。这就很清楚的说明了ThreadLocal的实际用法了。
但是为什么ThreadLocal能做到不同线程中维护一套数据的副本且不相互影响了。
ThreadLocal内部的实现原理:从set方法和get方法分析。
set( )分析:

    public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }

通过getMap中传递线程标志得到一个可以存储数据的ThreadLocalMap,不为null就存储,空的话就new一个ThreadLocalMap();下面我们来看看怎样用map.set(this,value)内部是怎样实现的吧。
红色关键部分。

private void set(ThreadLocal key, Object value) {            Entry[] tab = table;            int len = tab.length;            int i = key.threadLocalHashCode & (len-1);              /**     for (Entry e = tab[i]; e != null;e = tab[i = nextIndex(i, len)]) {                ThreadLocal k = e.get();                if (k == key) {                    e.value = value;                    return;                }           **/                    if (k == null) {                    replaceStaleEntry(key, value, i);                    return;                }            }            tab[i] = new Entry(key, value);            int sz = ++size;            if (!cleanSomeSlots(i, sz) && sz >= threshold)                rehash();        }

内部由一个Entry[ ]键值对数据来存储的,这键值对Entry < ThreadLoacl key,Object value >,通过一些算法等来判断将value值储存起来。 即内部有个存储value的数组Entry。
下面来分析get的方法

    public T get() {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null) {            ThreadLocalMap.Entry e = map.getEntry(this);            if (e != null)                return (T)e.value;        }        return setInitialValue();    }

代码逻辑也很清晰。通过线程标志获取对应的ThreadLocalMap,接着获取内部的键值对Entry数据,然后取出值。
总结:从set和get的从可以分析出他们都是操作当前线程的ThreadLocalMap内部的键值对Entry数组,,所有的存储和读取数据都是在各自的对应的线程标志内完成。通过分析可以更加清楚上面的小例子中输出结果共用一个对象但互不影响他们在各自线程中数据的存取的原因了。

0 0