Android之了解ThreadLocal

转载请标明出处:【顾林海的博客】

个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持!
在这里插入图片描述

##前言
很长时间没写博客,不是自己懒,而是在这段时间在思考要写什么,以及自己的发展方向,之前的自己很浮躁,总想表现什么,这其实对技术人来说是不好的,把心沉淀下来,找准方向,再一步步去实现,幸运的是自己又找到自我,这篇文章就讲讲一个小知识点ThreadLocal。


强调

ThreadLocal与多线程并发没有任何关系,ThreadLocal解决的是线程读写各自内部对象的问题,而多线程并发是指临界资源被多个线程访问从而导致数据的一致性
的问题。


作用

  • 解决线程内部对象访问的问题。
  • 避免对象作为参数到处传递。

使用

提供两个对外的方法:get()和set()方法用于内部对象的读写操作。

mBooleanThreadLocal.set(true);
Log.d(TAG, "[Thread#main]mBooleanThreadLocal=" + mBooleanThreadLocal.get());

new Thread("Thread#1") {
	@Override
	public void run() {
		mBooleanThreadLocal.set(false);
		Log.d(TAG, "[Thread#1]mBooleanThreadLocal=" + mBooleanThreadLocal.get());
	};
}.start();

new Thread("Thread#2") {
	@Override
	public void run() {
		Log.d(TAG, "[Thread#2]mBooleanThreadLocal=" + mBooleanThreadLocal.get());
	};
}.start();

在主线程中输出true,在Thread#1中输出false,在Thread#2中输出null。
总结,不同线程访问同一个ThreadLocal对象,ThreadLocal对象维护一套数据的副本并且彼此互不干扰。


工作原理

get方法

get() 方法源码如下:

public T get() {
    //以当前线程作为getMap()方法的参数。
    Thread t = Thread.currentThread();
    //获取当前线程保存ThreadLocal值的ThreadLocalMap对象。
    ThreadLocalMap map = getMap(t);

    if (map != null) {

        ThreadLocalMap.Entry e = map.getEntry(this);

        if (e != null)

            return (T)e.value;

    }

    return setInitialValue();

}

ThreadLocalMap getMap(Thread t) {
    //返回当前线程的ThreadLocalMap对象。
    return t.threadLocals;

}


上面代码中get()方法有三个个分支路线可走:

  • 获取到当前线程的ThreadLocalMap,从中获取值,如果获取的值存在直接返回。
  • 获取到当前线程的ThreadLocalMap,从中获取值,如果获取的值不存在执行setInitialValue()方法。
  • 获取到当前线程的ThreadLocalMap,发现ThreadLocalMap对象为空,执行setInitialValue()方法。

如果能直接获取值当然好了,但获取不到值就只能执行setInitialValue()方法:

private T setInitialValue() {

    T value = initialValue();

    Thread t = Thread.currentThread();

    ThreadLocalMap map = getMap(t);

    if (map != null)
        //获取到当前线程的ThreadLocalMap,从中获取值,如果获取的值不存在。
        map.set(this, value);

    else
        //获取到当前线程的ThreadLocalMap,发现ThreadLocalMap对象为空
        createMap(t, value);

    return value;

}

protected T initialValue() {
     return null;
}

分析上面三个分支路线的后两种:

  • 获取到当前线程的ThreadLocalMap,从中获取值,如果获取的值不存在执行setInitialValue()方法。

ThreadLocalMap对象存在,但获取不到值时,setInitialValue()方法就会执行map.set(this,value)方法用于初始化,最终会返回null

  • 获取到当前线程的ThreadLocalMap,发现ThreadLocalMap对象为空,执行setInitialValue()方法。

ThreadLocalMap对象都不存在,会间接的执行createMap(t,value)方法,用于创建ThreadLocalMap对象,如下:

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

值得注意的是上面的initialValue()方法是protected类型的,也就是说开发人员可以在创建ThreadLocal时重写initialValue方法,以满足特定
的业务需求。

set方法

    public void set(T value) {
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程保存ThreadLocal值的ThreadLocalMap对象。
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

根据当前线程获取的ThreadLocalMap对象,当ThreadLocalMap对象存在就保存value,否则创建ThreadLocalMap对象并保存value。


应用场景

  • 解决多线程内部对象的访问
  • 避免对象作为参数到处传递。

在Android的Handler机制中Looper类使用ThreadLocal保存对应线程的Looper实例。

Handler机制在线程的套路:

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}

工作线程中使用Looper之前必须调用prepare()方法初始化。

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //保存当前线程的Looper实例
        sThreadLocal.set(new Looper(quitAllowed));
    }

prepare()就保存了每个工作线程的Looper实例,这样Handler内部就能获取到当前线程的Looper并发送消息。

注意

    static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
        省略相关代码
    }

ThreadLocalMap 在内部实现时对于 ThreadLocal 中对象的引用使用的是弱引用类型,从而规避内存泄漏的风险。

©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:上身试试 返回首页