Remember to read the article trilogy, like, comment, and retweet.

Wechat search [programmer Xiao An] attention is still in the mobile development field of the elderly programmers, “interview series” articles will be published in the public account.

1. Introduction

Recently see the network all say now inside the volume serious, the interview is very difficult, as the appearance level of the talented young _ also began the way of the interview, since said that the interviewer each is elite, very unfortunately, Lao Tze hit is elite.

2. The body

Talented young _ confidence full to some east conference room, waiting for an interview, decided to discuss with them.

Looper objects use ThreadLocal to ensure that each thread has a unique Looper object, and that threads do not interact with each other.

Yes, it is. So direct, no foreplay, looks like it’s time for real. ThreadLocal provides a separate copy of the variable for each thread that uses it, so each thread can independently change its own copy without affecting the corresponding copies of other threads, thereby achieving thread isolation.

Tell me about the use of ThreadLocal in Looper.

So much talk about the original Looper source, ha ha, this is my strength.

1) Initialize a ThreadLocal:

 // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
Copy the code

2) Call the set method to store the Looper object of the current thread, call the get method to obtain the Looper object of the current thread:

private static void prepare(boolean quitAllowed) {
        if(sThreadLocal.get() ! =null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
Copy the code

Let’s talk about how ThreadLocal works.

I knew I would ask. Fortunately, that night I read the source code with Han in the office, and she secretly told me that she had my child. No, I think I was looking at the source code that night. Anyway, I was trying to remember the source code for ThreadLocal. 1) Let’s look at the set method of ThreadLocal:

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

Get ThreadLocalMap (); insert value into ThreadLocalMap ();

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

Get the threadLocals variable from the Thread object. Let’s see what threadLocals is, which goes directly to the Thread class:

class Thread implements Runnable {
    /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null; . }Copy the code

Each Thread contains a ThreadLocalMap object that stores the Looper. In this way, when each thread stores a Looper object into a ThreadLocal, it is stored in a ThreadLocalMap object within each thread, thus preventing other threads from retrieving the Looper object and implementing thread isolation.

Now that we’re there, tell me about ThreadLocalMap.

Ask, anyway that night is very long, we together in addition to read the source code, also did not do other things, as to the child how come, I can only say THAT I am an honest person, I do not know what.

1) Take a look at the constructor and key member variables of ThreadLocalMap:

        /** * The table, resized as necessary. * table.length MUST always be a power of two. */
        privateEntry[] table; 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);
        }
Copy the code

2) Entry[] table shows that although it is called ThreadLocalMap, the underlying storage is not based on HashMap, but in the form of array. Bah, men who cheat and play with women’s feelings, duplicity. Let’s not look at his appearance. Let’s look inside him.

 static class Entry extends WeakReference<ThreadLocal<? >>{
            /** The value associated with this ThreadLocal. */Object value; Entry(ThreadLocal<? > k, Object v) {super(k); value = v; }}Copy the code

As you can see, although it is not stored in the form of a Hashmap, Entry objects are also designed to resolve hash conflicts in the form of keys/values. So you can think of a ThreadLocalMap as an array, and the objects stored in the array are entries in the form of keys/values.

I’m sorry to interrupt, but I have a couple of questions here, and the first one is why is it an array?

When ThreadLocalMap is returned to the client, it returns only one object, but it must return an array. The engineer who wrote ThreadLocalMap is one of my colleagues. The set method of a ThreadLocal (ThreadLocal) is used to set a ThreadLocal (ThreadLocal).

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

ThreadLocalMap (Looper, value); getMap (); this (key); Looper (value); ThreadLocalMap is designed to be an array. Some threads have more than one ThreadLocal object in them and may initialize more than one ThreadLocal object, so you need an array to store it. To understand how a ThreadLocalMap is stored, let’s take a look at the set method of a ThreadLocalMap.

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

Int I = key.threadLocalHashCode & (len-1); This code is consistent with the hash of the key in the hashmap. The purpose of this code is to resolve hash conflicts and find arrays to insert subscripts.

If the key already exists in the array, the value will be overwritten to the vaule in the array:

if (k == key) {
    e.value = value;
    return;
}
Copy the code

If key is empty, an Entry object is created and placed in that position:

if (k == null) {
    replaceStaleEntry(key, value, i);
    return;
}
Copy the code

If neither of the above conditions is satisfied, it looks for the next position I and continues the loop until it finds a position that can be inserted or refreshed.

e = tab[i = nextIndex(i, len)]
Copy the code

By the way, let’s also talk about get methods.

The service will certainly be a full set, without you asking, I will also talk about the logic of get method, this is our mechanic (technical work) professional ethics. 1) The ThreadLocal get method is as follows:

public T get(a) {
        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;
                returnresult; }}return setInitialValue();
    }
Copy the code

Get ThreadLocalMap (t), getEntry (t), Enety (t), ThreadLocalMap (t), Enety (T);

 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
           return getEntryAfterMiss(key, i, e);
}
Copy the code

int i = key.threadLocalHashCode & (table.length – 1); TreadLocal getEntryAfterMiss getEntryAfterMiss getEntryAfterMiss getEntryAfterMiss getEntryAfterMiss getEntryAfterMiss getEntryAfterMiss getEntryAfterMiss getEntryAfterMiss getEntryAfterMiss getEntryAfterMiss

 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)
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }
Copy the code

The code is easy to understand. If the current ThreadLocal is equal to the key of the Entry object corresponding to array subscript I, the current Entry object is returned. If the key of the Entry object corresponding to the array subscript I is empty, the expungeStaleEntry(I) method is executed. From the name of the method, we know that deleting the discarded Entry corresponding is actually a memory reclaim.

 private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;

            // expunge entry at staleSlot
            tab[staleSlot].value = null;
            tab[staleSlot] = null;
            size--;

            // Rehash until we encounter null
            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);
                    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;
        }
Copy the code

Let’s focus on the following lines of code:

if (k == null) {
     e.value = null;
     tab[i] = null;
Copy the code

What this method does is it frees the value object and the Entry object if the key of an Entry object in the array is empty. If the key of the Entry object corresponding to the array subscript I is neither equal nor empty, the nextIndex method is called and the nextIndex method is searched down, as is the nextIndex method of the set method.

Let’s go a little further and talk about why the key of an Entry object is set to weak reference. And is there a memory leak in ThreadLocal?

In a traditional interview, it’s about finishing the interview until it’s done, and I pass it. If I brag hard, HE can be fooled. The young man is rude. Come on! Cheat! Come on! Roll in my old client, is that ok? This is not good, I advise, the interviewer, rattail juice, reflect, do not give this kind of interview questions in the future, IT people should be valued, thank you!

Since the interview, I must be with Xiao Han alone with several nights, no, is to see the source of several nights. Let’s review the Entry constructor again:

static class Entry extends WeakReference<ThreadLocal<? >>{
            /** The value associated with this ThreadLocal. */Object value; Entry(ThreadLocal<? > k, Object v) {super(k); value = v; }}Copy the code

As you can see from the constructor, the key in the Entry object, or ThreadLocal object, is a weak reference.

Weak references: When the garbage collector thread scans the memory area under its control, once it finds an object with only weak references, it reclaims its memory regardless of whether the current memory space is sufficient. However, because the garbage collector is a low-priority thread, objects that have only weak references are not necessarily found quickly.

Read the following passage carefully several times and highlight it.

If key is not a WeakReference WeakReference, if a thread is in an infinite loop, ThreadLocalMap will always exist and reference ThreadLocal, causing that ThreadLocal cannot be released and value cannot be released. When WeakReference is WeakReference, even if the thread is dead loop, when the place where ThreadLocal is created is released, the key of ThreadLocalMap will also be released. When calling getEntry, it will judge that if the key is null, the value will be released. Memory leaks do not exist. Of course, the ThreadLocalMap class also provides the remove method, which will remove the Entry of the current ThreadLocal without leaking memory. So if I personally feel that if I manually call the remove method every time I don’t need to use the ThreadLocal, There are no memory leaks.

Well, that’s pretty good, that’s pretty good, so let’s go back to the representation and say why Looper objects should be in ThreadLocal, why can’t we share one, or hold one per thread?

Sure enough, he was a senior interviewer. The question went from simple to profound, and then came back to the essence of the question. This technical ability was worthy of his shedding of hair.

First of all, I think technically, Looper objects can share a global, that is, every thread can share the same Looper object, but for the sake of thread safety, we need to carry out thread synchronization processing, such as adding synchronization lock, so that the operation efficiency will be reduced, on the other hand, if the Android system does not process Looper messages within 5 seconds, ANR will result, and adding a synchronization lock will increase the chance of ANR.

As for why not hold a Looper object per thread, it’s easy to understand: to save memory. If you only have two threads, there’s no advantage to using ThreadLocal. If you’re going to initialize 1000 threads, and each thread initializes Looper objects, then you’re going to have 1000 Looper objects, which is a lot of memory overhead, and we know that with multiple threads, Threads are often added to a thread pool, as in:

ExecutorService threadPool = Executors.newFixedThreadPool(8);
Copy the code

ThreadLocal provides a separate copy of the variable for each thread that uses it, so each thread holding one Looper object initializes eight Looper objects. Using ThreadLocal saves memory.

Ok, I’m impressed by your comprehensive understanding of ThreadLocal. Go back and wait for the offer.


Wechat search [programmer xiao an] “interview series (java&andriod)” article will be published in the public account.