美文网首页技术干货
白话ThreadLocal,看这一篇就足够了!

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

作者: Top2_头秃 | 来源:发表于2019-04-26 17:04 被阅读0次

    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的内部类)

    • 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{}  // 内部类
    }
    
    • Thread类
    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 的存活周期就是线程的生命周期

    相关文章

      网友评论

        本文标题:白话ThreadLocal,看这一篇就足够了!

        本文链接:https://www.haomeiwen.com/subject/cmdqnqtx.html