当前位置:网站首页>ThreadLocal source code learning

ThreadLocal source code learning

2022-07-05 09:46:00 Black Mountain demon 2018

I'm learning EventBus When I saw the source code ThreadLocal, After consulting relevant materials, we know ThreadLocal There are quite a lot of places to use , So simply analyze it here ThreadLocal Principle and problems of

What is? ThreadLocal

ThreadLocal Is a tool class that can help threads store copies of variables , Why is it a variable copy , Because when multiple threads access the same variable ,ThreadLocal Each thread will be mapped to this variable one by one ( Note that the one-to-one mapping here is just a pseudo concept ), In this way, each thread corresponds to a copy of this variable , They don't influence each other , Mutual interference , Especially in the case of high concurrency , Each thread can pass through ThreadLocal Get the corresponding information to operate .

Realize the idea

see Thread Source code can be found ,Thread There is one in the class ThreadLocal.ThreadLocalMap A member variable of type threadLocals, That is, each thread has a separate ThreadLocal.ThreadLocalMap. and ThreadLocal.ThreadLocalMap There's a Entry Array ,Entry It's a key-value Form storage class , among key yes ThreadLocal Weak references to objects ,value Is the copy variable to be saved .ThreadLocal I'm giving everyone Thread When saving data, it is based on the current ThreadLocal The weak reference of the instance is key, Insert data into your ThreadLocalMap among , So different threads are based on key( The same ThreadLocal Weak references to instances ) Reading and writing data do not affect each other , Mutual interference .

Now let's take a look at ThreadLocal Of Code chip

ThreadLocal The core approach

// Get the copy variable value stored by the current thread , If the current thread does not store this value , Then return to initialValue() Method return value 
public T get()

// Initialize the copy variable of the current thread 
private T setInitialValue()

// Set the copy variable of the current thread 
public void set(T value)

// Remove the copy variable of the current thread 
public void remove() 

// Initialize the copy variable of the current thread  
protected T initialValue()

ThreadLocal Core inner class

public T get()

public T get() {
    
    // Get the current thread 
    Thread t = Thread.currentThread();
    // Gets the current thread's ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    if (map != null) {
    
       // adopt ThreadLocal Object acquisition Entry object 
       ThreadLocalMap.Entry e = map.getEntry(this);
       if (e != null) {
    
          @SuppressWarnings("unchecked")
          T result = (T)e.value;
             return result;
          }
       }
       // If map by null, Then build map
        return setInitialValue();
   }

    private Entry getEntry(ThreadLocal<?> key) {
    
        int i = key.threadLocalHashCode & (table.length - 1);
        Entry e = table[i];
        // If e Not for null And key Not recycled and e Of key Value weak reference points to ThreadLocal And key equal 
        // Then return to the entry object 
        if (e != null && e.get() == key)
           return e;
        else
           // Otherwise, continue the linear search 
           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();
                // Find the target , Go straight back to entry object 
                if (k == key)
                    return e;
                // If key The value points to ThreadLocal Object is recycled , Then hold expungeStaleEntry
                // Clean up 
                if (k == null)
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }

    // because staleSlot Positional entry Object's key The pointing area has been recycled , So manually put 
    //tab[staleSlot] as well as tab[staleSlot].value Set as null
    // The second thing , from staleSlot To traverse the , For non empty on each position entry Adjust the position 
	private int expungeStaleEntry(int staleSlot) {
    
    	Entry[] tab = table;
    	int len = tab.length;

    	// because key ThreadLocal The weak reference of has been set to null Being recycled or about to be recycled , So will 
    	//entry Of value Also set to null,entry Also set to null
    	tab[staleSlot].value = null;
    	tab[staleSlot] = null;
    	//size reduce 1
    	size--;

     	Entry e;
	    int i;
     	for (i = nextIndex(staleSlot, len);(e = tab[i]) != null;i = 		nextIndex(i, len)) {
    
        	ThreadLocal<?> k = e.get();
        	if (k == null) {
    
        	// If key ThreadLocal The weak reference of has been set to null Being recycled , Will 
        	//entry Of value Also set to null,entry Also set to null, Simultaneous size reduction 1
           		e.value = null;
           		tab[i] = null;
           		size--;
         	} else {
    
          	// If not recycled , Then get k Of hash value , Determine whether it is the current position ,
          	// If not , From k The position starts traversing until it is found entry by null When , Will the current i Positional 
          	//entry Assign values to the tab[h] On , At the same time, the current i Positional entry The object is set to null
           int h = k.threadLocalHashCode & (len - 1);
           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;
   }

private T setInitialValue()

private T setInitialValue() {
    
    // Get the initial default value 
     T value = initialValue();
     Thread t = Thread.currentThread();
     ThreadLocalMap map = getMap(t);
        if (map != null)
           map.set(this, value);
        else
           createMap(t, value);
        return value;
    }

  // You can see here that Thread Of ThreadLocalMap Member variable assignment , Enter again ThreadLocalMap Constructor to see 
  void createMap(Thread t, T firstValue) {
    
      t.threadLocals = new ThreadLocalMap(this, firstValue);
  }

  private static final int INITIAL_CAPACITY = 16;
 
  ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    
      // The size is 16 Of entry An array of objects 
      table = new Entry[INITIAL_CAPACITY];
      //firstKey.threadLocalHashCode And INITIAL_CAPACITY Modulo get hash value , It is also the position in the array 
      int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
      table[i] = new Entry(firstKey, firstValue);
      size = 1;
      setThreshold(INITIAL_CAPACITY);
  }

public void remove()

public void remove() {
    
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
      // according to ThreadLocal Object delete variable copy 
      m.remove(this);
}

private void remove(ThreadLocal<?> key) {
    
    // obtain entry Array 
    Entry[] tab = table;
    // Get array length 
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);
    // Traversing an array , When it finds key when , perform e.clear(), Show weak references set to null
    // At the same time expungeStaleEntry() Method 
    for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {
    
        if (e.get() == key) {
    
            e.clear();
            expungeStaleEntry(i);
            return;
         }
     }
  }

public void set(T value)

public void set(T value) {
    
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
       map.set(this, value);
    else
       createMap(t, value);
 }

//ThreadLocalMap set
private void set(ThreadLocal<?> key, Object value) {
    
     Entry[] tab = table;
     int len = tab.length;
     int i = key.threadLocalHashCode & (len-1);

     // Traverse entry Array 
     for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {
    
         ThreadLocal<?> k = e.get();
		
		 // If we find the corresponding key, Then cover the original value
         if (k == key) {
    
            e.value = value;
            return;
          }
          // If key Has been recovered , Replace those that are about to expire entry
          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 The inner class of 
static class ThreadLocalMap {
    

    // You can see here Entry Class is key-value Data structure of , also Entry(key) Is holding 
    //ThreadLocal The soft references 
	static class Entry extends WeakReference<ThreadLocal<?>> {
    
       /** The value associated with this ThreadLocal. */
       Object value;

       Entry(ThreadLocal<?> k, Object v) {
    
          super(k);
          value = v;
        }
     }
     
     // Initial capacity , It has to be for 2 The power of 
     private static final int INITIAL_CAPACITY = 16;
	 //entry Array , The size must be 2 The power of 
     private Entry[] table;
     //entry Array size 
     private int size = 0;
     
     private int threshold; // Default to 0
}
   // Return to the next index 
    private static int nextIndex(int i, int len) {
    
       return ((i + 1 < len) ? i + 1 : 0);
    }
    
    // Return to previous index 
    private static int prevIndex(int i, int len) {
    
       return ((i - 1 >= 0) ? i - 1 : len - 1);
    }

It can be seen that ThreadLocalMap Maintenance of tab Array is a ring structure , Every object in the array is Entry object (key-value Structure ), One ThreadLocalMap Can store multiple ThreadLoad object key value .

ThreadLocal The problem

ThreadLocal.ThreadLocalMap Use ThreadLocal Weak reference of object as Entry Of key, once ThreadLocal Object is recycled , namely key by null, If there are hundreds stored in a thread ThreadLoad Object space , Then there is key by null Of Entry There will be more and more objects , Plus, there is always the possibility that this thread will not end , At this time, memory leakage will occur .

So here comes the question , At this time, it will be considered as weak reference key Value causes Entry Objects that cannot be reclaimed and thus memory leaks . It's not , Let's look at the following two situations :
1.key Use strong references : It's recycling ThreadLocal The object is , however ThreadLocalMap And hold ThreadLocal A strong reference to , If it is not manually set to null, be ThreadLocal Will not be recycled , Lead to Entry Memory leak .
2.key Using weak references : It's recycling ThreadLocal The object is , because ThreadLocalMap hold ThreadLocal The weak references , Even if it is not manually set to null,ThreadLocal It will also be recycled .value The next time ThreadLocalMap call set,get,remove Will be cleared when .

So the essence of the problem is that there is no manual key Set as null.
ThreadLocal It's by putting key Set to weak reference , And at the same time use set,get,remove Method to manually delete key by null Of Entry Object to avoid memory leaks . When ThreadLocal A lot of storage Key by null Of Entry When , Instead of calling remove、get、set Method , That will lead to a memory leak .

原网站

版权声明
本文为[Black Mountain demon 2018]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202140536454402.html