Analysis of ThreadLocal and threadlocal

Source: Internet
Author: User

Analysis of ThreadLocal and threadlocal

This is my first blog. It is not very clear, but I hope it will help you.

First, clarify the role of this class. The ThreadLocal class is used to provide a copy of the variable for each thread, that is, the local variable of each thread. Every thread has the value of this variable in its own stack space (the object is referenced), and they are separated by threads, modifications made by a thread to this ThreadLocal variable will not be seen by other threads.

The following is an example.

1 import java. util. random; 2 3 public class ThreadLocalTest {4 private static final ThreadLocal <Integer> threadId = new ThreadLocal <Integer> () {5 protected Integer initialValue () {6 return 6; 7} 8}; 9 10 public static int get () {11 return threadId. get (); 12} 13 14 public static void main (String [] args) {15 new Thread (new MThread ("Thread ")). start (); 16 new Thread (new MThread ("Thread B ")). start (); 17 new Thread (new MThread ("Thread C ")). start (); 18} 19 20 static class MThread implements Runnable {21 private String name; 22 public MThread (String str) {23 name = str; 24} 25 public void run () {26 int id = get (); 27 System. out. println ("thread" + name + "'s Id:" + id); 28 threadId. set (10 * new Random (). nextInt (10); 29 for (int I = 0; I <3; I ++) {30 id = get (); 31 System. out. println ("** thread" + name + "'s Id:" + id); 32} 33} 34} 35}

Result:

Thread B's Id: 6
** Thread B's Id: 60
** Thread B's Id: 60
** Thread B's Id: 60
Thread A's Id: 6
** Thread A's Id: 50
** Thread A's Id: 50
** Thread A's Id: 50
Thread C's Id: 6
** Thread C's Id: 90
** Thread C's Id: 90
** Thread C's Id: 90

From the above results, we can see that the value of get () is 6 for each thread at the beginning, and from the output order, we can see that thread A calls get () for the first time () thread B has set () over threadId in 28 rows, but the result of A is 6, which indicates that the change of thread B is invisible to thread.

Then let's look at the comments in the source code:

The general idea is that each thread has its own copy of the variable independent of others' initialization. The following uses example in the source code to check whether the initialization is independent.

1 import java. util. concurrent. atomic. atomicInteger; 2 3 public class ThreadLocalId {4 private static final AtomicInteger nextId = new AtomicInteger (0); 5 private static final ThreadLocal <Integer> threadId = new ThreadLocal <Integer> () {6 protected Integer initialValue () {7 return nextId. getAndIncrement (); 8} 9}; 10 11 public static int get () {12 return threadId. get (); 13} 14 15 public static void main (String [] args) {16 new Thread (new MThread ("Thread ")). start (); 17 new Thread (new MThread ("Thread B ")). start (); 18 new Thread (new MThread ("Thread C ")). start (); 19} 20 21 static class MThread implements Runnable {22 private String name; 23 public MThread (String str) {24 name = str; 25} 26 public void run () {27 int id; 28 for (int I = 0; I <3; I ++) {29 id = get (); 30 System. out. println ("thread" + name + "'s Id:" + id); 31} 32} 33} 34}

Result:

Thread A's Id: 1
Thread A's Id: 1
Thread C's Id: 2
Thread C's Id: 2
Thread B's Id: 0
Thread B's Id: 0
Thread B's Id: 0
Thread A's Id: 1
Thread C's Id: 2

The results show that the values of each thread are different because they are independently initialized. Because the first time they call get (), if they have not set () or remove () before, they will call the initialValue () method to initialize the value, this method overwrites the 6th rows and returns the value of nextId each time. The value of nextId is automatically increased every time it is obtained, so the value of each thread will be different. The preceding loop is used three times to illustrate the sentence in the source code comment: remains unchanged on subsequent cals, and the subsequent call will return the unchanged value (of course, the premise is that the set () changed value ..).

This can be seen from the get () source code:

public T get() {  Thread t = Thread.currentThread();
  ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue();}

 

First, we will get the thread object that is currently calling the get () method, and then get the ThreadLocalMap object bound to this thread through the getMap () method. The following is the implementation of getMap ().

ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }

It is easy to obtain the ThreadLocal. ThreadLocalMap threadLocals = null member variable of the Thread class. The default value is null in the Thread class. When the Map is empty, setInitialValue () is called ();

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

In setInitialValue (), the overwritable initialValue () is called to obtain a value for the value. Just because the Map is null, this method is called, so createMap (t, value), and this method is to assign a new ThreadLocalMap object to the parameter t thread. The first of the construction parameters, this is the Threadlocal object, which corresponds to a value in the Entry.

void createMap(Thread t, T firstValue) {        t.threadLocals = new ThreadLocalMap(this, firstValue);    }ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {            table = new Entry[INITIAL_CAPACITY];            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);            table[i] = new Entry(firstKey, firstValue);            size = 1;            setThreshold(INITIAL_CAPACITY);        }

 

We can see that ThreadLocalMap holds an array of entries, and each Entry has a ThreadLocal corresponding to a value. Then we can know the general idea of combining the get () source code:To obtain the value of the threadLocal variable, first find the current thread object, and then obtain the threadLocalMap member variable of the thread object, then, use the Entry [] In threadLocalMap to obtain the Entry object corresponding to the current ThreadLocal, and then the Entry. value gets the final value.The following code shows how to find the Entry object corresponding to the current Thread:

 1 ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { 2             table = new Entry[INITIAL_CAPACITY]; 3             int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); 4             table[i] = new Entry(firstKey, firstValue); 5             size = 1; 6             setThreshold(INITIAL_CAPACITY); 7         } 8  9 private Entry getEntry(ThreadLocal key) {10             int i = key.threadLocalHashCode & (table.length - 1);11             Entry e = table[i];12             if (e != null && e.get() == key)13                 return e;14             else15                 return getEntryAfterMiss(key, i, e);16         }

When rows 4th are put, the subscript obtained by the threadLocalHashCode member of ThreadLocal is initialized through the bitwise AND upper Entry array capacity-1. Therefore, when getEntry () is used, the row 10th first obtains the subscript I, then table [I] can get the Entry.

In my own words:Each thread has a ThreaLocalMap variable. This Map contains an Entry array, which stores many mappings between ThreadLocal and Value. When you call get, it is found that the thread creates a ThreadLocalMap without ThreadLocalMap. If the Entry corresponding to ThreadLocal is not found in the Entry array, an Entry is created and the value is obtained (the value is obtained in the initialValue) just put it in and return the value.It can be seen that each thread uses a space to store this variable. Therefore, the thread is isolated and does not interfere with each other.

The set () method is similar. You can see the source code.

The remove () method proactively deletes the Entry corresponding to threadLocal, which is no longer used, to avoid Memory leakage.

GetEntryAfterMiss () is also called in getEntry () to ensure that stale (obsolete) Entries can be cleared and the memory leakage can be solved to a large extent.

Here, as long as the thread is still alive, there is an implicit reference to the threadLocal variable. The reference chain is thread-> threadLocalMap-> Entry-> threadLocal. After the thread ends, all of its local instance copies of the thread will be recycled by GC (unless other copies are referenced ).

Thank you very much for seeing your comments or suggestions at the end. You are welcome to point out that I will make positive improvements.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.