directory

  1. What is a ThreadLocal
  2. How to use ThreadLocal
  3. ThreadLocal source code analysis
  4. Use of ThreadLocal in Looper in messaging mechanisms
  5. data
  6. harvest

In the last article, we analyzed the implementation of Anrdoid message mechanism. However, we are still not clear about ThreadLocal and Native layer. In this article, we will study and analyze the functions of ThreadLocal.

What is ThreadLocal

ThreadLocal is a generic class that can accept objects of any type. Normally, ThreadLocal variables are static.

We know that different threads have their own stacks, but memory resources are shared in the same process, meaning that different threads can access the same variable, and there are multithreaded synchronization problems. If a variable is modified by one thread and read by another thread, it may result in inconsistent results from different threads if it is not locked or volatile.

Imagine a scenario where the following two conditions are met

  1. A variable in an object, it’s going to be used in different methods, it’s going to be called in different threads.
  2. This variable does not require multi-thread synchronization, but rather requires a separate value for each thread, that is, thread isolation.

So how do we design this? Perhaps the first thing that comes to mind is to store the Thread (eg: ThreadId) in the form of Map, Key, and Value for each Thread. To achieve the requirements of the above scenario, add a synchronization lock to the map read/write operations. However, this solution will bring certain performance loss due to the addition of locks. Is there a better way to achieve thread isolation?

The ThreadLocal examined today is designed for this purpose, and is suitable for scenarios where each thread needs its own independent instance and that instance needs to be used in multiple methods, that is, variables are isolated between threads and shared between methods or classes.

Variables decorated with ThreadLocal have their own copy within each thread and can only be used in their own thread, implementing thread isolation.

How to use ThreadLocal

In this section, we use a simple test code to illustrate ThreadLocal’s use and validation of its thread isolation features.

Define variables of type ThreadLocal in a class, assign different values to different threads, and output to see if there is any effect between threads.

Public class ExampleUnitTest {// Define two different types of ThreadLocal private static ThreadLocal<String> sStrThreadlocal = new ThreadLocal<>(); private static ThreadLocal<Integer> sIntegerThreadLocal = new ThreadLocal<>(); @Test public void addition_isCorrect() { assertEquals(4, 2 + 2); } @test public void testThreadLocal(){sstrThreadlocal.set ("aaa"); String value = sStrThreadlocal.get(); sIntegerThreadLocal.set(1); int intValue = sIntegerThreadLocal.get(); System.out.println("111 curThreadId="+Thread.currentThread()+" strthreadLocalValue="+value +" intThreadLocalValue="+intValue); New Thread(new Runnable() {@override public void run() {sstrThreadLocal.set (" BBB "); String value = sStrThreadlocal.get(); sIntegerThreadLocal.set(2); int intValue = sIntegerThreadLocal.get(); System.out.println("222 curThreadId="+Thread.currentThread()+" strthreadLocalValue="+value +" intThreadLocalValue="+intValue); } }).start(); new Thread(new Runnable() { @Override public void run() { String value = sStrThreadlocal.get(); Integer intValue = sIntegerThreadLocal.get(); System.out.println("333 curThreadId="+Thread.currentThread()+" strthreadLocalValue="+value +" intThreadLocalValue="+intValue); } }).start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // The main thread value value = sstrThreadlocal.get (); intValue = sIntegerThreadLocal.get(); System.out.println("444 curThreadId="+Thread.currentThread()+" strthreadLocalValue="+value +" intThreadLocalValue="+intValue); }}Copy the code

The running results are as follows:

111 curThreadId=Thread[main,5,main] strthreadLocalValue=aaa intThreadLocalValue=1 222 CurThreadId = Thread/Thread - 0, 5, the main strthreadLocalValue = BBB intThreadLocalValue = 2, 333 CurThreadId = Thread/Thread - 1, 5, the main strthreadLocalValue = null intThreadLocalValue = null 444 curThreadId=Thread[main,5,main] strthreadLocalValue=aaa intThreadLocalValue=1Copy the code

Different threads assign different values to variables modified by ThreadLocal, and each thread gets a different value, thus achieving thread isolation.

So how does it do that? Is it possible to use HashMap to store values from different threads? Let’s analyze the ThreadLocal source code to find out.

ThreadLocal source code analysis

ThreadLocal is a generic class that can accept objects of any type

As shown in the sample code above, multiple ThreadLocal objects can exist within a thread, and a ThreadLocal maintains a Map to meet this requirement. But this Map is not a HashMap used directly, but rather a static inner class of ThreadLocal’s implementation called ThreadLocalMap

In the example above, we can see that ThreadLocal is set to set data and get to get data. We use this as an entry point for analysis

ThreadLocal#set

Public void set(T value) {public void set(T value) {T = thread.currentThread (); ThreadLocalMap map = getMap(t); // If the thread has a copy of the ThreadLocal, store it in the map, key, otherwise create if (map! = null) map.set(this, value); else createMap(t, value); }Copy the code

getMap

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

Look at the Thread class, found threadLocals type of variable is a ThreadLocal. ThreadLocalMap, namely ThreadLocal a static inner class

Each Thread object maintains a ThreadLocalMap that can hold several ThreadLocal’s

public class Thread implements Runnable { ...... / / the current thread ThreadLocalMap, mainly ThreadLocal to store the thread itself, this article mainly discuss the ThreadLocal variable. ThreadLocalMap threadLocals = null; / / ThreadLocalMap inherited from a parent thread, it is mainly used for thread between father and son ThreadLocal variable transmission ThreadLocal. ThreadLocalMap inheritableThreadLocals = null; . }Copy the code

Look at the ThreadLocal. ThreadLocalMap

   static class ThreadLocalMap {
        .......
        private ThreadLocal.ThreadLocalMap.Entry[] table;
Copy the code

ThreadLocal.ThreadLocalMap.Entry

The key of the Entry is a weak reference to ThreadLocal, and the value is the value of the thread-local variable SET in the corresponding thread. We know that a weak reference will destroy the object it wraps around during GC. This threadLocal can be destroyed as a key (if no strong reference exists), and if the key is empty, the entry will be removed from the table

static class Entry extends WeakReference<ThreadLocal<? >> { Object value; Entry(ThreadLocal<? > var1, Object var2) { super(var1); this.value = var2; }}Copy the code

ThreadLocal memory leak Prevention (ThreadLocal)

Essentially, each thread maintains a map whose key is a threadLocal and whose value is the value of our set

After analyzing the SET link, let’s look at the GET link

When we call get(), we get the current thread first, then get the current thread’s ThreadLocalMap object, if not empty, then fetch the value of ThreadLocal, otherwise initialize it. Initialization is to set the value of initialValue into a ThreadLocal.

Public T get() {T = thread.currentThread (); ThreadLocalMap map = getMap(t); if (map ! Threadlocalmap.entry e = map.getentry (this); // If threadLocalMap.entry e = map.getentry (this); if (e ! = null) { T result = (T)e.value; return result; Return setInitialValue(); // Set initialValue () to ThreadLocal; }Copy the code

Use of ThreadLocal in Looper

ThreadLocal is used in many places in the Android Framework, Looper, Choreographer, ActivityThread, ContentProvide, ViewRootImpl, SQLiteDatabase, and more can also be used for invocation chain tracing. Let’s analyze the Looper for each thread to be guaranteed to be independent, and a thread to have only one Looper

public final class Looper { ...... // sthreadLocal.get () returns null until prepare() is called. // sThreadLocal is an instance of ThreadLocal, Looper static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); final MessageQueue mQueue; . }Copy the code

Thread isolation via ThreadLocal ensures that Looper is different for each thread, via sthreadlocal.get ()! The null exception assertion ensures that only one Looper // initialization can occur per thread, initializing the current thread as a circulator

Private static void prepare(Boolean quitAllowed) {// Make sure that the Looper of each thread is different with ThreadLocal, // make sure that the Looper of each thread is different with sthreadLocal-get ()! If (sthreadLocal.get ()!) = null, a thread can only have one Looper if (sthreadLocal.get ()! = null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } /** * returns the Looper of the thread on which the method was called * If the caller's thread is not already associated with Looper (via preprea), */ public static @nullable Looper myLooper() {return sthreadLocal.get (); }Copy the code

Five, the data

  1. The Android source code

  2. ThreadLocal,

  3. InheritableThreadLocal,

  4. Principle analysis and application scenarios of ThreadLocal

  5. Confident, this is ThreadLocal analysis at its best

  6. Epoll summary for IO multiplexing

  7. Android MessageQueue nativePollOnce

  8. Class ‘kotlin.Unit’ was compiled with an incompatible version of Kotlin

  9. ThreadLocal memory Leak Prevention (ThreadLocal)

Six, harvest

Through the study and practice of this article

  1. Understand the significance and principle of ThreadLocal
  2. ThreadLocal is suitable for scenarios where each thread needs its own independent instance and that instance needs to be used in multiple methods, that is, variables are isolated between threads and shared between methods or classes.
  3. The Android messaging mechanism uses ThreadLocal to ensure Thread isolation of Looper. Get is the assertion that a Thread can only have one Looper.

Thank you for reading the next article we analyze the Native layer of message mechanism, analyze and understand how to block and wake up, welcome to follow the public account “audio and video development journey”, learn and grow together. Welcome to communicate