ThreadLocal源码分析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ThreadLocal源码分析相关的知识,希望对你有一定的参考价值。

什么是ThreadLocal:

官网介绍:

* This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.

看另一个介绍:

The ThreadLocal class in Java enables you to create variables that can only be read
and written by the same thread. Thus, even if two threads are executing the same
code, and the code has a reference to a ThreadLocal variable, then the two threads
cannot see each other‘s ThreadLocal variables. 

简单点说:就是ThreadLoacal类能够创建被同一个线程进行读和写的变量。并且执行相同代码并且代码中包含ThreadLocal变量的引用的两个不同线程不能互相看到对方的ThreadLocal的变量。

看了官网的介绍,我们会似懂非懂,那么ThreadLocal是如何实现的呢?

ThreaLocal example
package com.dp.test;

import java.util.Scanner;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        myThread t1=new myThread(false);
        myThread t2=new myThread(true);

        t1.setName("Thread1");
        t2.setName("Thread2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }

}
class myThread extends Thread {

    private ThreadLocal<String> local=new ThreadLocal<String>(){

        @Override
        protected String initialValue() {
            // TODO Auto-generated method stub
            return "threadLocal的初始化";
        }

    };
    private boolean read=false;
    public myThread(boolean read){
        this.read=read;
    }
    public void run(){
        if(read==false){
            local.set(Thread.currentThread().getName()+"local");
            System.out.println(local.get());
        }else
        {
            System.out.println(local.get());
        }

    }
}

result》》》》》》》》 
Thread1local
threadLocal的初始化

从代码的运行结果看出 每个线程都有自己的ThreadLocal变量,其中T1的设置为Thread1local 而T2没有set,当我们调用get时T1有,T2只有初始化的变量。现在我们来看看ThreadLocal的源码吧!

ThreadLocal源码分析 :

ThreadLocal 中的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);
    }

步骤:

  • 调用set()方法时先得到当前正在运行的线程t
  • 调用getMap(t)得到ThreadLocalMap 的对象
  ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

我们来看看t.threadLocals这个变量

 /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

threadLocals是Thread类中的一个成员变量。

  • 当map不为空时则调用 map.set(this, value);那我们来看看这个方法
 private void set(ThreadLocal<?> key, Object value) {

            // We don‘t use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            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();
        }

该方法是ThreadLocal内部类ThreadLocalMap中的方法,有关于ThreadLocalMap的结构下次再讲,我们可以类比喻hashmap,存入键值对的。

  • 如果map为空,这是我们就的创建对象 createMap(t, value);
  void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

关于set方法的流程总结 :

  local.set(Thread.currentThread().getName()+"local");
   ---》getMap(t)得到ThreadLocalMap对象
         ---》t.threadLocals
              ---》threadLocals是Thread类中的一个成员变量
   ---》判断map是否为空
      --->不为空:调用ThreadLocalMap的set方法
      ---》为空:调用createMap方法

ThreadLocal的get方法

  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();
    }

根据前面的讲解我们应该对get方法中一些函数有点理解,对于get方法我就简单讲解下,

local.get()
 ---》调用getMap(t)得到ThreadLocalMap的对象
 ---》判断map是否为空
   ---》不为空,调用map.getEntry(this);就是根据ThreadLocal得到Entry,得到其值。
   ---》为空,调用setInitialValue()方法将初始值返回。

我们看看setInitialValue(方法

 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;
    }

从方法体中,我们可以看出该方法做了两件事,一用初始值创建ThreadLocalMap对象
二 返回初始值

以上是关于ThreadLocal源码分析的主要内容,如果未能解决你的问题,请参考以下文章

ThreadLocal源码分析

ThreadLocal源码分析理解弱引用和内存泄漏

ThreadLocal源码分析理解弱引用和内存泄漏

ThreadLocal 源码分析

ThreadLocal源码分析理解弱引用和内存泄漏

ThreadLocal源码分析_01 入门案例以及表层源码分析