"Increased capacity salary increase may be" ThreadLocal -Java multithreading and concurrency of

1. ThreadLocal What is that? scenes to be used

ThreadLocal Profile

ThreadLocal thread-local variables, you can provide a solution for the problem of concurrent multi-threaded, when using ThreadLocal variable maintenance, use thread ThreadLocal provide a copy of the variable for each independent variable, so each thread can be changed independently own copy without affecting other threads corresponding copy.

ThreadLocal usage scenarios

When multiple threads to acquire a shared variable, the requirements to obtain a copy of the initial value of this variable. A copy of each thread memory of this variable, the variable changes do not affect copies of the variable itself. For multiple threads to complete the operation dependent variable values ​​of different scenes. such as:

  • Switching multiple data sources
  • spring declarative transaction

2. ThreadLocal use cases

ThreadLocal class interface:

  • Values ​​in the current thread's thread-local variables: void set (T value)
  • T get (): Get the current thread corresponding to the thread-local variables
  • () Void remove: Delete the current value of the thread-local variables, the purpose is to reduce memory consumption
  • T initialValue (): if the initial value of the thread local variable (default null value), which is a protected lazy loading method, the thread calls get () or set (T value) in the first execution, but also to make a subclass cover design.
public class ThreadLocalDemo {
    private static ThreadLocal<Index> index = new ThreadLocal(){
        @Override
        protected Object initialValue() {
            return new Index();
        }
    };

    private static class Index{
        private int num;

        public void incr(){
            num++;
        }
    }

    public static void main(String[] args) {
        for(int i=0; i<5; i++){
            new Thread(() ->{
                Index local = index.get();
                local.incr();
                System.out.println(Thread.currentThread().getName() + " " + index.get().num);
            }, "thread_" + i).start();
        }
    }
}
复制代码

Output:

thread_1 1 thread_0 1 thread_3 1 thread_4 1 thread_2 1

We can see the initial value obtained for each thread is 0, and the operation of the num ++ also affect each other

How to achieve 3.ThreadLocal

3.1 ThreadLocal data structure

ThreadLocal internal maintenance is a data structure similar ThreadLocalMap Map, and each of the Thread class, there is a ThreadLocalMap member variable. ThreadLocalMap the thread-local variables (the ThreadLocal) as a copy key, a thread variable as the value, as shown:

 

 

 

Note that the relationship between the key and the value of the Entry of ThreadLocal in systematic maintenance, improper maintenance could result if unsafe in multithreaded state (generally not, at least note).

3.2 get () source code analysis

  public T get() {
      	//获取当前线程
          Thread t = Thread.currentThread();
      	//获取当前线程的ThreadLocalMap
          ThreadLocalMap map = getMap(t);
          if (map != null) {
              //如果ThreadLocalMap已经被创建了,那么通过当前的threadLocal对象作为key,获取value
              ThreadLocalMap.Entry e = map.getEntry(this);
              if (e != null) {
                  @SuppressWarnings("unchecked")
                  T result = (T)e.value;
                  return result;
              }
          }
      	//如果ThreadLocalMap还没有被创建或者在ThreadLocalMap中查找不到此元素
          return setInitialValue();
      }
复制代码

3.2.1 ThreadLocalMap did not initialize

ThreadLocalMap not initialized, ThreadLocalMap is null, calls setInitialValue () method:

  private T setInitialValue() {
      	//initialValue方法一般会被重写,不重写的话,直接返回null
          T value = initialValue();
          Thread t = Thread.currentThread();
      	//获取当前线程的ThreadLocalMap
          ThreadLocalMap map = getMap(t);
          if (map != null)
              //ThreadLocalMap已经被创建,那么直接设置初始值(即保存变量副本),初始值来自initialValue方法
              map.set(this, value);
          else
              //创建ThreadLocalMap
              createMap(t, value);
          return value;
      }
复制代码

Which, initialValue () method is overridden by our need to note that the return value must be a new target, instead of directly returns an object reference. Because if multiple threads are stored with a copy of the reference, then they cite the value of shared variables modified through this, they influence each other. Our original purpose is to obtain a copy of the initial value of the shared variable, each thread changes to the copy does not affect the variable itself.

Let's look at how to create threadLocalMap of createMap

  void createMap(Thread t, T firstValue) {
          t.threadLocals = new ThreadLocalMap(this, firstValue);
      }
复制代码
  ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
      	   //创建一个初始容量为16的Entry数组
              table = new Entry[INITIAL_CAPACITY];
      		//通过threadLocal的threadLocalHashCode来定位在数组中的位置
              int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
      		//保存在数组中
              table[i] = new Entry(firstKey, firstValue);
      		//记录下已用的大小
              size = 1;
      		//设置阈值为容量的2/3
              setThreshold(INITIAL_CAPACITY);
          }
复制代码

3.2.2 initialization threadLocalMap

After initialization threadLocalMap, this thread calls the get () method again, and what does it do

  public T get() {
      	//获取当前线程
          Thread t = Thread.currentThread();
      	//获取当前线程的ThreadLocalMap
          ThreadLocalMap map = getMap(t);
          if (map != null) {
              //如果ThreadLocalMap已经被创建了,那么通过当前的threadLocal对象作为key,获取value
              ThreadLocalMap.Entry e = map.getEntry(this);
              if (e != null) {
                  @SuppressWarnings("unchecked")
                  T result = (T)e.value;
                  return result;
              }
          }
      	//如果ThreadLocalMap还没有被创建或者在ThreadLocalMap中查找不到此元素
          return setInitialValue();
      }
复制代码

Can be seen by map.getEntry (this) to find elements

  private Entry getEntry(ThreadLocal<?> key) {
              int i = key.threadLocalHashCode & (table.length - 1);
              Entry e = table[i];
              if (e != null && e.get() == key)
                  return e;
              else
                  //如果定位的元素的key与传入的key不相等,那么一直往后找
                  return getEntryAfterMiss(key, i, e);
          }
复制代码
  private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
              Entry[] tab = table;
              int len = tab.length;
  
              while (e != null) {
                  ThreadLocal<?> k = e.get();
                  if (k == key)
                      return e;
                  if (k == null)
                      //将过期的key清除掉,并把后面的元素(移动过位置的)往前移
                      expungeStaleEntry(i);
                  else
                      //往后移一位
                      i = nextIndex(i, len);
                  e = tab[i];
              }
              return null;
          }
复制代码
  private static int nextIndex(int i, int len) {
              return ((i + 1 < len) ? i + 1 : 0);
          }
复制代码
  private int expungeStaleEntry(int staleSlot) {
      Entry[] tab = table;
      int len = tab.length;
  
      // 清除当前元素
      tab[staleSlot].value = null;
      tab[staleSlot] = null;
      size--;
  
      //将此元素后面的,因为hash冲突移动过位置的元素往前移
      Entry e;
      int i;
      for (i = nextIndex(staleSlot, len);
           (e = tab[i]) != null;
           i = nextIndex(i, len)) {
          ThreadLocal<?> k = e.get();
          if (k == null) {
              e.value = null;
              tab[i] = null;
              size--;
          } else {
              int h = k.threadLocalHashCode & (len - 1);
              //h != i说明有过hash冲突
              if (h != i) {
                  tab[i] = null;
  
                  // Unlike Knuth 6.4 Algorithm R, we must scan until
                  // null because multiple entries could have been stale.
                  while (tab[h] != null)
                      h = nextIndex(h, len);
                  tab[h] = e;
              }
          }
      }
      return i;
  }
复制代码

If map.getEntry (this) could not find the elements of how to do?

  public T get() {
      	//获取当前线程
          Thread t = Thread.currentThread();
      	//获取当前线程的ThreadLocalMap
          ThreadLocalMap map = getMap(t);
          if (map != null) {
              //如果ThreadLocalMap已经被创建了,那么通过当前的threadLocal对象作为key,获取value
              ThreadLocalMap.Entry e = map.getEntry(this);
              if (e != null) {
                  @SuppressWarnings("unchecked")
                  T result = (T)e.value;
                  return result;
              }
          }
      	//如果ThreadLocalMap还没有被创建或者在ThreadLocalMap中查找不到此元素
          return setInitialValue();
      }
复制代码

So we continue to call setInitialValue () method

  private T setInitialValue() {
      	//initialValue方法一般会被重写,不重写的话,直接返回null
          T value = initialValue();
          Thread t = Thread.currentThread();
      	//获取当前线程的ThreadLocalMap
          ThreadLocalMap map = getMap(t);
          if (map != null)
              //ThreadLocalMap已经被创建,那么直接设置初始值(即保存变量副本),初始值来自initialValue方法
              map.set(this, value);
          else
              //创建ThreadLocalMap
              createMap(t, value);
          return value;
      }
复制代码

I can see it will be called inside map.set (this, value) method

  private void set(ThreadLocal<?> key, Object value) {
              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;
                  }
              }
  			
      		//如果在table中确实找不到,那么新建一个
              tab[i] = new Entry(key, value);
              int sz = ++size;
              if (!cleanSomeSlots(i, sz) && sz >= threshold)
                  //如果没有元素被清除,且超过阈值,那么扩容并重新hash定位
                  rehash();
          }
复制代码

3.3 set () source code analysis

See in the set () and setInitialValue similar manner, the initialization value less obtain other are the same, reference may get () mode [ThreadLocalMap not initialized] (# 3.2.2 Initialization threadLocalMap):

public void set(T value) {
    //获取当前线程
    Thread t = Thread.currentThread();
    //获取当前线程的ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
复制代码

3.4 summary

  • First, determine the current thread threadLocalMap exists, if there is to create one. The ThreadLocal as a copy of the key, the initial value of the shared variable as the value, stored in the threadLocalMap
  • If threadLocalMap exist, then ThreadLocal as a key, a hash by hash positioning position in the array, look for value
  • If no (reason: to be deleted, or added to threadLocalMap this ThreaLocal not, after all, threadLocalMap can store multiple threadLocal) in threadLocalMap, then the ThreadLocal as a key, a copy of the initial value of the shared variable as the value, stored in the threadLocalMap
  • During the set, if the current position of the other elements (ie, hash conflict), then looking back, until there is no other elements. And will remove some elements expired (key is null) in the process set in. Finally, according to the size of size, to decide whether to expansion, re-hash positioning

 

Published 139 original articles · won praise 18 · views 749

Guess you like

Origin blog.csdn.net/qq_45401061/article/details/103892276