ThreadLocal Explanation of android Handler Mechanism

Summary

When we talk about Handler mechanism, we actually talk about the relationship among Handler, Message, Looper and MessageQueue. We don't elaborate on its working principle.( Detailed explanation of Handler mechanism).

  • Message: Message objects that Handler sends, receives, and processes
  • Looper: Each thread can only have one Looper. Its looper() method is responsible for looping messages in MessageQueue and forwarding the read messages to the handler sending the message for processing.
  • MessageQueue: Message queue, which manages messages in a first-in-first-out manner. When a program creates a Looper object, it creates a MessageQueue in its constructor.

Brief Analysis of Handler Classes

The Handler class has two main functions: sending messages in newly started threads and getting and processing messages in the main threads.
To fully understand the Handler mechanism, it is necessary to understand Looper's underlying storage and polling mechanism. After looking at it, it is very simple. Today I will talk about it specifically.

ThreadLocal Explanation

For your understanding, let's look directly at the source code:

public class ThreadLocal<T> {
 .....
}

Here we can see that threadlocal is a paradigm class, which indicates that threadlocal can store all data. As a storage data, the first thing we think about is to provide set(),get(),remove(), etc.
set() method:

 /**
     * Sets the value of this variable for the current thread. If set to
     * {@code null}, the value will be set to null and the underlying entry will
     * still be present.
     *
     * @param value the new value of the variable for the caller thread.
     */
    public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }

As you can see from the source code, first get the current thread, then call the values method. Let's look at the values method:

/**
     * Gets Values instance for this thread and variable type.
     */
    Values values(Thread current) {
        return current.localValues;
    }

This method returns a stored real class of the current thread. What about ThreadLocal? As mentioned above, ThreadLocal is a data storage class within a thread, through which data can be stored in a specified thread. After data storage, only the stored data can be obtained in the specified thread.
Let's look at a few ThreadLocal methods. First, we go back to the set method. After we get the class of values, we'll make a judgment and call initializeValues (current Thread) for null.

 Values initializeValues(Thread current) {
        return current.localValues = new Values();
    }

Next we call the put method of value, and we should think of interpolating into it, which is what we call put().

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

From the source code, we can see that the value of values is passed to the next subscript of key.reference of a table array. So far, we understand the stored value process of Threadlocal. First, we get the current thread. According to the current thread, we get the Values storage class, which is singleton in the thread and in the calling value storage class. The put method ultimately stores the stored content into the table array of the inner class of Values, labeled key.reference.
Next, let's look at the method of value selection:

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

The logic of the get method of ThreadLocal is also clear. It also takes out the localValues object of the current thread and returns the initial value if the object is null. The initial value is described by the initial Value method of ThreadLocal.

  protected T initialValue() {
        return null;
    }

If the localValues object is not null, then take out its table array and find out where the reference object of ThreadLocal is in the table array. Then the next location in the table array stores the value of ThreadLocal.
Next, let's look at remove's implementation.

 void remove(ThreadLocal<?> key) {
            cleanUp();

            for (int index = key.hash & mask;; index = next(index)) {
                Object reference = table[index];

                if (reference == key.reference) {
                    // Success!
                    table[index] = TOMBSTONE;
                    table[index + 1] = null;
                    tombstones++;
                    size--;
                    return;
                }

                if (reference == null) {
                    // No entry found.
                    return;
                }
            }
        }

So we have a complete understanding of the ThreadLocal access principle: ThreadLocal set and get methods show that the objects they operate on are the table array of the localValues object of the current thread, so access the set and get methods of the same ThreadLocal in different threads, and they read and write to the ThreadLocal. Operations are limited to the internals of individual threads, which is why ThreadLocal can store and modify data in multiple threads without interfering with each other.

Posted by xluminex on Sun, 07 Jul 2019 19:48:37 -0700