Working principle of ThreadLocal in Android message mechanism, androidthreadlocal
We should be familiar with the messaging mechanism, which is inevitably involved in daily development. From the perspective of development, Handler is the upper-layer interface of the Android message mechanism, so that you only need to interact with Handler during development. Handler is easy to use. It can be used to easily switch a task to the thread where the Handler is located for execution. Many people think that Handler is used to update the UI. This is true, but updating the UI is only a special use case of Handler. Specifically, it is like this: sometimes it is necessary to perform time-consuming IO operations in the Child thread, which may be reading files or accessing the network. After time-consuming operations are completed, some changes may need to be made on the UI, due to the limitations of Android development specifications, we cannot access the UI control in the Child thread. Otherwise, a program exception is triggered. In this case, Handler can switch the UI update operation to the main thread for execution. Therefore, Handler is not specifically used to update the UI, but is often used to update the UI.
Android message mechanism mainly refers to the Handler operation mechanism. Handler operation requires the support of the underlying MessageQueue and logoff. The Chinese Translation of MessageQueue is a message queue. As its name suggests, it stores a group of messages internally, and it provides external insertion and deletion in the form of a queue, although called a message queue, however, its internal storage structure is not a real queue, but a single linked table data structure is used to store the message list. Logoff can be understood as a message loop. MessageQueue is only a unit of storage for messages and cannot process messages. logoff fills in this function, logoff searches for new messages in an infinite loop. If yes, It processes the messages. Otherwise, it waits. Another special concept in logoff is ThreadLocal. ThreadLocal is not a thread. It can store data in each thread. As we all know, when Handler is created, it uses the Logoff of the current thread to construct the message loop system. How can Handler obtain the Logoff of the current thread internally? This requires ThreadLocal. ThreadLocal can store and provide data in different threads without interfering with each other. With ThreadLocal, you can easily obtain the Logoff of each thread. Of course, it should be noted that the thread does not have logoff by default. If Handler needs to be used, you must create logoff for the thread. The main thread that we often mention is also called the UI thread, which is the ActivityThread. When the ActivityThread is created, logoff is initialized. This is also the reason why Handler can be used by default in the main thread.
ThreadLocal is a data storage class within a thread. It can store data in a specified thread. After data is stored, the stored data can be obtained only in a specified thread, data cannot be obtained by other threads. ThreadLocal is rarely used in daily development. However, in some special scenarios, ThreadLocal can easily implement complex functions, this is also reflected in the Android Source Code. For example, ThreadLocal is used in logoff, ActivityThread, and AMS. It is difficult to describe ThreadLocal in a uniform way. Generally, when some data is in the thread scope and different threads have different data copies, ThreadLocal can be considered. For example, for Handler, it needs to obtain the Looper of the current thread. Obviously, the Looper scope is the thread and different threads have different Looper, in this case, ThreadLocal can be used to easily implement logoff in the thread. If ThreadLocal is not used, the system must provide a global hash table for Handler to find the Logoff of the specified thread, in this way, a class similar to LooperManager must be provided, but the ThreadLocal is selected instead. This is the benefit of ThreadLocal.
Another Application Scenario of ThreadLocal is the transfer of objects in complex logic, such as the delivery of listeners. Sometimes, tasks in a thread are too complex, this may be manifested in the deep function call stack and the diversity of code entries. In this case, we need the listener to be able to run throughout the entire thread execution process. What can we do at this time? In fact, ThreadLocal can be used. ThreadLocal can be used to make the listener exist as a global object in the thread. Within the thread, the listener can be obtained through the get method. If ThreadLocal is not used, we can think of the following two methods: the first method is to pass the listener in the function call stack in the form of parameters, the second method is to use the listener as a static variable for thread access. Both methods have limitations. When the function call stack is very deep, it is almost unacceptable to pass the listener object through function parameters, which makes the program design look terrible. The second method is acceptable, but this state is not extensible. For example, if two threads are running at the same time, two static listener objects must be provided, what if 10 threads are executing concurrently? 10 static listener objects? This is obviously incredible. With ThreadLocal, each listener object is stored in its own thread, so there is no such problem as method 2.
The introduction of so many ThreadLocal knowledge may be a bit abstract. The following uses actual examples to demonstrate the true meaning of ThreadLocal. First, define a ThreadLocal object. Here, select the Boolean type, as shown below:
Private ThreadLocal <Boolean> mBooleanThreadLocal = new ThreadLocal <Boolean> ();
Set and access its values in the main thread, subthread 1, and subthread 2 respectively. The Code is as follows:
mBooleanThreadLocal.set(true);Log.d(TAG, "[Thread#main]mBooleanThreadLocal=" + mBooleanThreadLocal.get());new Thread("Thread#1") {@Overridepublic void run() {mBooleanThreadLocal.set(false);Log.d(TAG, "[Thread#1]mBooleanThreadLocal=" + mBooleanThreadLocal.get());};}.start();new Thread("Thread#2") {@Overridepublic void run() {Log.d(TAG, "[Thread#2]mBooleanThreadLocal=" + mBooleanThreadLocal.get());};}.start();
In the above Code, set mBooleanThreadLocal to true in the main thread, set mBooleanThreadLocal to false in sub-thread 1, and do not set mBooleanThreadLocal in sub-thread 2, then, get the mBooleanThreadLocal value in the three threads respectively. According to the previous description of ThreadLocal, the value in the main thread should be true and that in Sub-thread 1 should be false, because no value is set in sub-thread 2, it should be null. Install and run the program. The log is as follows:
D/TestActivity (8676): [Thread # main] mBooleanThreadLocal = true
D/TestActivity (8676): [Thread #1] mBooleanThreadLocal = false
D/TestActivity (8676): [Thread #2] mBooleanThreadLocal = null
As shown in the preceding logs, although the same ThreadLocal object is accessed in different threads, the value obtained through ThreadLocal is different, which is the wonder of ThreadLocal. Combining this example and looking at the theoretical analysis of the two Use Cases of ThreadLocal, we should be able to better understand how to use ThreadLocal. ThreadLocal has such a wonderful effect because different threads access the get method of the same ThreadLocal, and ThreadLocal extracts an array from their respective threads, then we can find the corresponding value from the array based on the current ThreadLocal index. Obviously, the arrays in different threads are different, this is why ThreadLocal can maintain a set of data copies in different threads and do not interfere with each other.
After introducing the usage and working process of ThreadLocal, the internal implementation of ThreadLocal is analyzed below. ThreadLocal is a generic class, which is defined as public class ThreadLocal <T>, as long as you understand the get and set methods of ThreadLocal, you can understand how it works.
First, let's look at the set method of ThreadLocal, as shown below:
public void set(T value) {Thread currentThread = Thread.currentThread();Values values = values(currentThread);if (values == null) {values = initializeValues(currentThread);}values.put(this, value);}
In the above set method, we will first use the values method to obtain ThreadLocal data in the current thread. What if we get it? In fact, the method of obtaining is also very simple. In the Thread class content, there is a member dedicated to storing ThreadLocal data of the Thread, as shown below: ThreadLocal. values localValues. Therefore, it is very easy to obtain the ThreadLocal data of the current thread. If the localValues value is null, You need to initialize it and store the ThreadLocal value after initialization. Next, let's take a look at how the ThreadLocal value is stored in localValues. There is an array inside localValues: private Object [] table. The value of ThreadLocal exists in this table array, next, let's take a look at how localValues uses the put Method to store ThreadLocal values in the table array, as shown below:
void put(ThreadLocal<?> key, Object value) {cleanUp();// Keep track of first tombstone. That's where we want to go back// and add an entry if necessary.int firstTombstone = -1;for (int index = key.hash & mask;; index = next(index)) {Object k = table[index];if (k == key.reference) {// Replace existing entry.table[index + 1] = value;return;}if (k == null) {if (firstTombstone == -1) {// Fill in null slot.table[index] = key.reference;table[index + 1] = value;size++;return;}// Go back and replace first tombstone.table[firstTombstone] = key.reference;table[firstTombstone + 1] = value;tombstones--;size++;return;}// Remember first tombstone.if (firstTombstone == -1 && k == TOMBSTONE) {firstTombstone = index;}}}
The code above implements the data storage process. We will not analyze its specific algorithms here, but we can come up with a storage rule, that is, the storage location of the ThreadLocal value in the table array is always the next location of the object identified by the reference field of ThreadLocal. For example, the index of the ThreadLocal reference object in the table array is index, then the index of ThreadLocal value in the table array is index + 1. The value of ThreadLocal will be stored in the table array: table [index + 1] = value.
The set Method of ThreadLocal is analyzed above. Here, the get method is analyzed as follows:
public T get() {// Optimized for the fast path.Thread currentThread = Thread.currentThread();Values values = values(currentThread);if (values != null) {Object[] table = values.table;int index = hash & values.mask;if (this.reference == table[index]) {return (T) table[index + 1];}} else {values = initializeValues(currentThread);}return (T) values.getAfterMiss(this);}
It can be found that the logic of the get method of ThreadLocal is clear. It is also used to retrieve the localValues object of the current thread. If this object is null, the initial value is returned, the initial value is described by the initialValue method of ThreadLocal. The default value is null. You can also rewrite this method. Its default implementation is as follows:
/** * Provides the initial value of this variable for the current thread. * The default implementation returns {@code null}. * * @return the initial value of the variable. */protected T initialValue() {return null;}
If the localValues object is not null, retrieve its table array and locate the position of the ThreadLocal reference object in the table array, the data stored in the next position in the table array is the value of ThreadLocal.
From the set and get methods of ThreadLocal, we can see that all the objects they operate on are the table arrays of the localValues object of the current thread. Therefore, the set and get methods of the same ThreadLocal are accessed in different threads, their read and write operations on ThreadLocal are limited to the internal operations of their respective threads. This is why ThreadLocal can store and modify data in multiple threads without interfering with each other, understanding the implementation of ThreadLocal helps to understand how logoff works.
Copyright Disclaimer: This article is an original article by the blogger and cannot be reproduced without the permission of the blogger.