[Android Interview Questions] Android Framework Core Interview Questions - The principle of ThreadLocal and how it is applied in Looper?

The principle of ThreadLocal and how it is applied in Looper? (ByteDance, Xiaomi)

What do you want to examine in this question?

ThreadLocal must be mastered. This is because the working principle of Looper has a lot to do with ThreadLocal. Understanding the implementation of ThreadLocal will help us understand the working principle of Looper. This article will start with the usage of ThreadLocal. Take everyone step by step to understand ThrealLocal.

Knowledge points to investigate

  1. The inner workings of ThreadLocal
  2. Looper related knowledge

How should candidates answer

ThreadLocal can save an object in a specified thread. After the object is saved, the saved data can only be obtained in the specified thread, and the data cannot be obtained by other threads. ThreadLocal is rarely used in daily development, but the system uses it in the Handler mechanism to ensure that each Handler thread has an independent Looper object in order to better understand the Handler mechanism.

What is ThreadLocal

ThreadLocal is a class about creating thread local variables.

In fact, the scope of this variable is the thread and cannot be accessed by other threads. Usually the variables we create can be accessed by any thread, but the variables created using ThreadLocal can only be accessed by the current thread and cannot be accessed by other threads.

So how does ThreadLocal ensure that only the current thread can access it? Let's first analyze the two most important functions in ThreadLocal, get() and set().

First look at the source code in the get() method

public T get() {
    
    
	Thread t = Thread.currentThread();  //code 1
	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();
}

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

ThreadLocalMap getMap(Thread t) {
    
    
	return t.threadLocals;
}
    /**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    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;
    }

From code1 and code2, it should be easy to find that when setting or getting the value of ThreadLocal, the current thread will be obtained first, and then getMap(thread) will be called based on the thread. What getMap returns is the member variable threadLocals of the thread thread. Therefore, the corresponding function is executed through both get and set, which ensures that access to threadLocal can only access or modify the value of the current thread, which ensures that this variable is a local variable of the thread.

So what is ThreadLocalMap next?

What is ThreadLocalMap
static class ThreadLocalMap {
    
    
    static class Entry extends WeakReference<ThreadLocal<?>> {
    
    
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
    
    
            super(k);
            value = v;
        }
    }
    private Entry[] table;
    //省略部分代码
    }

It can be seen from the source code that ThreadLocalMap is an array, and the array contains Entry inherited from weak references. Weak references are also used so that memory can be reclaimed when abnormal situations occur, such as infinite loops.

Let’s go back and look at the source code of 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);
}

The code here is very clear. It takes out the ThreadLocalMap according to the current thread, and then performs the operation of storing data. If the Map is empty, it is created first and then assigned.

Explore map.set()

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

    tab[i] = new Entry(key, value);
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}

Int i = key.threadLocalHashCode & (len-1) in the code is the same as the hash value method of key in HashMap, mainly to avoid hash value conflicts. Going further down, there are three situations when traversing the Map: 1. Find an existing key that is valid, and then assign a value; 2. Find an existing key but it is invalid, replace the expired Entry; 3. If the same key value is not found, create a new Entry and assign a value. .

So how is ThreadLocal used in Looper?

Application of ThreadLocal in Looper

Find the source code of Looper

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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

The relationship between Looper and threads is one-to-one. Looper objects are isolated between different threads. So how does Looper ensure this? Through the above code, it should be easy to find that the Looper initialization must call the prepare function. When the prepare function is called, the code will be executed to code 1. In code 1, it will first determine whether the value of the looper exists in the ThreadLocal of the current thread. If exists, then an exception is thrown. This execution ensures that a thread will only set Looper once. The execution flow of this code ensures that a thread has only one ThreadLocal, and a ThreadLocal has only one looper.

Summarize

ThreadLocal is a class that creates thread-local variables. Its implementation mechanism determines that the scope of this variable is the thread and cannot be accessed by other threads. Using this mechanism, you can ensure that a thread has only one ThreadLocal variable. Then, through the design of the prepare function in the looper, it is ensured that a ThreadLocal will only be bound to one Looper. Through these two methods, it is ensured that a thread has only one ThreadLocal variable, and a ThreadLocal variable has only one Looper, thus forming a one-to-one correspondence.


at last

I have compiled a collection of Android interview questions. In addition to the above interview questions, it also includes [ Java basics, collections, multi-threading, virtual machines, reflection, generics, concurrent programming, Android's four major components, asynchronous tasks and message mechanisms, UI drawing , performance tuning, SDN, third-party framework, design pattern, Kotlin, computer network, system startup process, Dart, Flutter, algorithm and data structure, NDK, H.264, H.265. Audio codec, FFmpeg, OpenMax, OpenCV, OpenGL ES ]
Insert image description here

Friends in need can scan the QR code below to receive all interview questions + answer analysis for free! ! !

Guess you like

Origin blog.csdn.net/datian1234/article/details/133495909