android开发:ThreadLocal使用场景与源码解析
阅读原文时间:2021年04月20日阅读:1

简介:

ThreadLocal可以实现线程内部存储数据,数据存储以后,只有指定线程可以得到存储数据。使用方式如下:

public class Main6Activity extends AppCompatActivity {
    ThreadLocal<String> threadLocal;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main6);
        threadLocal = new ThreadLocal<>();
        threadLocal.set("哈哈哈");
        Log.e("TAG",threadLocal.get());
        new Thread(new Runnable() {
            @Override
            public void run() {
                threadLocal.set("呵呵呵");
                Log.e("TAG",threadLocal.get());
            }
        }).start();
    }
}



2019-12-23 15:50:12.866 16793-16793/com.example.recyclearapplication E/TAG: 哈哈哈
2019-12-23 15:50:12.867 16793-16824/com.example.recyclearapplication E/TAG: 呵呵呵

可以看到同一个ThreadLocal对象在不同的线程中存取数据,获取的数据也将不同。接下来我们通过看源码分析:

get()方法源码:

  public T get() {
          //获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的ThreadLocalMap 对象
        ThreadLocalMap map = getMap(t);
        //不为空时获取值
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
  //获取当前线程的threadLocals变量
 ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

set()源码解析:

 public void set(T value) {
         //获取当前线程
        Thread t = Thread.currentThread();
         //获取当前线程的ThreadLocalMap 对象
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            //ThreadLocalMap 对象为null时创建
            createMap(t, value);
    }
  //为当前线程初始化ThreadLocalMap对象
  void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

ThreadLocalMap 其实是ThreadLocal的一个静态内部类,其存储结构类似于Map,它以键值对存储数据。而在java中每一个线程都维护了一个ThreadLocalMap 对象。因此ThreadLocal存取数据时其实就是先获取当前线程的ThreadLocalMap 对象,然后以ThreadLocal对象作为key值存到线程内部的ThreadLocalMap对象。因此我们使用同一个ThreadLocal对象在不同的线程中存数据获时取到的值也是不同,原因就是我们数据是存在不同的ThreadLocalMap对象中。

总结:

 new Thread(new Runnable() {
            @Override
            public void run() {
                fun1(context);
                threadLocal.set("呵呵呵");
                Log.e("TAG",threadLocal.get());
            }
        }).start();

    public void fun1(String context){
         //执行业务逻辑
         ...
         省略n行代码
         ...
        fun2(context);
    }
    public void fun2(String context){
       //执行业务逻辑
         ...
         省略n行代码
         ...
        fun3(context);
    }
    public void fun3(String context){
       //执行业务逻辑
         ...
         省略n行代码
         ...
        fun4(context);

    }
    public void fun4(String context){
   //执行业务逻辑
         ...
         省略n行代码
         ...
    }

ThreadLocal可以实现线程数据隔离,它适应在某些数据的作用域为线程的情况下。例如上诉代码我们在一个线程中执行异步操作,整个过程需要调用很多方法,并且我们每一个方法都需要传一个上下文参数,调用逻辑很复杂。这时候我们就可以使用ThreadLocal来解决。

 new Thread(new Runnable() {
            @Override
            public void run() {
                //threadLocal存数据
                threadLocal.set(context);
                Log.e("TAG",threadLocal.get());
                fun1();
            }
        }).start();

    }
    public void fun1(){
        String context = threadLocal.get();
        //执行业务逻辑
         ...
         省略n行代码
         ...
        fun2();
    }
    public void fun2( ){
       String context = threadLocal.get();
        //执行业务逻辑
         ...
         省略n行代码
         ...
        fun3();
    }
    public void fun3( ){
       String context = threadLocal.get();
        //执行业务逻辑
         ...
         省略n行代码
         ...
        fun4();

    }
    public void fun4(){
       String context = threadLocal.get();
        //执行业务逻辑
         ...
         省略n行代码
         ...

    }