技术干货

白话ThreadLocal,看这一篇就足够了!

2019-04-26  本文已影响0人  Top2_头秃

ThreadLoal是什么

ThreadLocal是变量的一种类型,比如你在主线程定义了一个变量String a,然后在这个主线程中使用Thread起了5个(序号分别是 1 2 3 4 5)子线程,如果每个线程中都用了这个a变量,那你就需要考虑线程之间的互相影响,因为线程2改变了a的值,那么在线程1中a的值也会变了。

我们如果把a的类型换成ThreadLocal<String>,那么线程2中改变a的值,并不会改变线程1中a的值,假设这5个子线程在运行之初,a的初始值都是madan,即使线程2中把a改成了mabi,在线程1中a的值还是madan(除非线程1中自己去改变a的值)

ThreadLoal能干什么

ThreadLocal的这种特性能干点什么实际意义的活呢?

  1. 减少同一个线程内多个函数或者模块之间变量的传递的复杂度(说实话目前我没有体验到同一个线程之间变量传递哪里复杂了!!!能复杂到哪去?)
  2. 解决并发下对临界资源的访问问题,这句话如果说人话,就是下面的解释
假如我有多个线程都要用到变量cao,这里的用到是指在在该变量的值载入线程开始,我这个线程就不希望这个值被别的线程改变了(当然线程自己可以修改)
使用ThreadLocal类型定义这个cao,就会省去很多麻烦

ThreadLoal为什么能干这些内容(即源码分析)

在此必须先吐槽一下网上的教程,都是来回复制粘贴的,来龙去脉屁都没有说清楚,还有的就是上来就是贴一堆源码,不能循序渐进的去介绍,看的人一脸懵逼啊~~~

要想搞明白ThreadLocal变量,必须搞明白以下三个类的调用关系
源码看不明白没关系,可以直接看下面的例子代码,然后对照着源码看

Thread类、ThreadLocal类、ThreadLocalMap类(这个类是ThreadLocal的内部类)

public class ThreadLocal<T> {
public ThreadLocal() {}  // 构造函数
  private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
  public T get() {
        Thread t = Thread.currentThread();
        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();
   }
  public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
   private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
  static class ThreadLocalMap{}  // 内部类
}
public class Thread implements Runnable {
      ThreadLocal.ThreadLocalMap threadLocals = null;
       public Thread(Runnable target) {
            init(null, target, "Thread-" + nextThreadNum(), 0);
      }
      // 其它代码

}

源码看不懂没有关系,现在来看一下演示例子,我会一步一步去解释

  public class TestThreadLocal { // 测试threadlocal的类

        private static final ThreadLocal<Integer> value = new ThreadLocal<Integer>() {

        //重写了ThreadLocal类中的initialValue方法
              protected Integer initialValue() {
                 return 0;
             }
         };

          public static void main(String[] args) {
                for (int i = 0; i < 5; i++) {
                // 这里使用的是Thread带参数的构造方法启动5个子线程,参找Thread类的源码
                    new Thread(new MyThread(i)).start();
                 }
          }
         //  实现Runnable接口
          static Mythread implements Runnable {  
                  private int index
                  // 带参数的构造方法
                  public Mythread(int i) {
                          this.index = i;
                  }
                //  重写overrirde runnable中的run方法
              public void run() {
                      System.out.println("线程" + index + "的初始value是:" + value.get());
                      for (int i = 0; i < 10; i++) {
                                // 这里使用了ThreadLocal中的set方法和get方法
                               value.set(value.get() + i);
                      }
                      System.out.println("线程" + index + "的累加value:" + value.get());
              }
          }
  }

上述代码的执行结果是

线程0的初始value:0
线程3的初始value:0
线程2的初始value:0
线程2的累加value:45
线程1的初始value:0
线程3的累加value:45
线程0的累加value:45
线程1的累加value:45

可见这5个线程中的value值是互相不影响的

ThreadLocal类型变量能达到这种目的的原因

用一句人话来解释:因为ThreadLocal类型的value变量(的值)是绑定在线程上的,即一个线程记录一份这个value。
Thread源码我们可以看到每个线程有个成员变量 ThreadLocal.ThreadLocalMap threadLocals = null;
在ThreadLocal中 获取到这个map 然后map.set(this, value); 这里的this就是ThreadLocal的实例(在我的测试代码中这个实例就是value),value值就是这个实例在这个线程中的值

即相当于每个thread线程中都有一个这样的k-vmap;其中k是每一个ThreadLocal实例,v就是这个ThreadLocal实例在这个线程中的值

ThreadLocal.ThreadLocalMap threadLocals 的存活周期就是线程的生命周期

上一篇 下一篇

猜你喜欢

热点阅读