One, foreword

This article doesn't cover Android, just the beginning!

Looper. Prepare static member variable sThreadLocal:

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

We see that if you get first, then you go to set.

Then I looked at the source code of ThreadLocal, and the JDK gave me another surprise:

/**
 * The difference between successively generated hash codes - turns
 * implicit sequential thread-local IDs into near-optimally spread
 * multiplicative hash values for power-of-two-sized tables.
 */
private static final int HASH_INCREMENT = 0x61c88647;
Copy the code

I found a very strange constant, but I didn’t know where it came from, so I did some research on the Internet to understand what this constant means and what ThreadLocal does.

Second, the ThreadLocal

What is this ThreadLocal? ThreadLocal is a thread’s internal storage class that stores data in a specified thread and is accessible only to the specified thread. Different threads cannot access this variable, so data isolation is achieved for the thread.

This class provides thread-local variables. These variables differ from their normal 
counterparts in that each thread that accesses one (via its {@code get} or {@code set} 
method) has its own, independently initialized copy of the variable.  {@code ThreadLocal} 
instances are typically private static fields in classes that wish to associate state with 
a thread (e.g., a user ID or Transaction ID).
Copy the code

ThreadLocal provides thread-local variables. Unlike other variables, only each thread can get/set, and each variable (copy) is independent.

2.1, ThreadLocal. Set

public class ThreadLocal<T> { public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map ! = null) map.set(this, value); // Map key = ThreadLocal, value is the object to store else createMap(t, value); } ThreadLocalMap getMap(Thread t) {} ThreadLocalMap getMap(Thread t) { ThreadLocal.ThreadLocalMap return t.threadLocals; } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }}Copy the code

2.2, ThreadLocal. Get

public class ThreadLocal<T> { public T get() { 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; return result; } } return setInitialValue(); // Initial value is null}}Copy the code

2.3, ThreadLocalMap

static class ThreadLocalMap { /** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference<ThreadLocal<? >> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<? > k, Object v) { super(k); value = v; Private static final int INITIAL_CAPACITY = 16; private static final int INITIAL_CAPACITY = 16; // Store key & Value private Entry[] table; Private int size = 0; Private int threshold; // table. Length * 2/3; }Copy the code

See online said, memory leaks: \ color {red} {see online said, memory leaks:} see online have said that memory leaks: Since ThreadLocalMap has the same lifetime as Thread (Thread holds threadLocals), when your key (ThreadLocal) is null, the value is still held by the table, therefore, You need to actively call ThreadLocal’s remove method to clear memory.

0x61C88647

As soon as we enter the ThreadLocal class, we see the following code:

public class ThreadLocal<T> { /** * ThreadLocals rely on per-thread linear-probe hash maps attached * to each thread (Thread.threadLocals and * inheritableThreadLocals). The ThreadLocal objects act as keys, * searched via threadLocalHashCode. This is a custom hash code * (useful only within ThreadLocalMaps) that eliminates collisions * in the common case where consecutively constructed ThreadLocals * are used by the same threads, while remaining well-behaved in * less common cases. */ private final int threadLocalHashCode = nextHashCode(); // 1640531527 /** * The next hash code to be given out. Updated atomically. Starts at * zero. */ private static AtomicInteger nextHashCode = new AtomicInteger(); /** * The difference between successively generated hash codes - turns * implicit sequential thread-local IDs into near-optimally spread * multiplicative hash values for power-of-two-sized tables. */ private static final int HASH_INCREMENT = 0x61c88647; // 1640531527 /** * Returns the next hash code. */ private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); }}Copy the code

HASH_INCREMENT is used to distribute the hash code evenly across a 2 to the N array. Each ThreadLocal uses this hash value!

What is a hash?

A Hash, also called a Hash, is a Hash algorithm that transforms an input of arbitrary length into an output of fixed length. The output value is the Hash value. In practice, different inputs may be hashed into the same output, which can cause conflicts. By using the HASH_INCREMENT mentioned above and a certain algorithm, the hash code can be evenly distributed in the array of 2 to the power of N, which ensures the dispersion of hash table and thus reduces the probability of conflict.

Hash algorithm

  • Modulus method

The hash length m, for a number p less than m modulo, the result is the hash address. The choice of P is very important, generally take prime number or m formula: F (k) = k % p (P <=m) because the modulus is actually obtained through a division operation, so it is called “modulus method”

  • Square hashing (square middle method)

First, the difference between close numbers is enlarged by finding the square value of the keyword, and then the middle number of digits is taken as the hash function value according to the length of the table. And because the middle digits of a product are related to each digit of the multiplier, the resulting hash address is more uniform. Formula: f(k) = ((k * k) >> X) << Y For common 32-bit integers, that is, f(k) = (k * k) >> 28

  • The Fibonacci hash method

Similar to square hashing, this method uses the value of the Fibonacci sequence as the multiplier rather than itself. For 16-bit integers, the multiplier is 40503. For 32-bit integers, the multiplier is 2654435769. For 64-bit integers, the multiplier is 11400714819323198485.

Formula: f(k) = (k * 2654435769) >> X) << Y

For a common 32-bit integer, f(k) = (k * 2654435769) >> 28, you can vaguely sense that 0x61C88647 has something to do with the Fibonacci sequence.

  • Random number method

Select a random function that takes the random value of the keyword as the hash address, usually used when the keyword length is different. Formula: F (k) = random(k)

  • Chain address method (zip method)

Now that we know the hashing algorithm, let’s look at the zipper method. In addition to the zipper method, which is used to reduce collisions in a HashMap, you can also use open addressing, rehash, chain addressing, public overflow areas, and so on. This is just a brief introduction to the zipper method.

Putting keyword (synonym) values with the same hash address in the same single linked list is called a synonym list. There are m hash addresses there are M linked lists, and the pointer array T[0..m-1] is used to store the head pointer of each linked list. All the records whose hash address is I are inserted into the single linked list with the pointer T[I] as a node. The initial values of each component in T shall be null Pointers.


for H a s h M a p \ color {red} {the HashMap}


For the mold method ( k = 16 \color{red}{for the mold method (k = 16)


Fibonacci hashing \color{red}{for Fibonacci hashing}

And you can see that the Fibonacci hashing is much better than the original division hashing.

Again, how do magic numbers come about?

public class HashTest {
    public static void main(String[] args) {
        long lg = (long) ((1L << 32) * (Math.sqrt(5) - 1)/2);
        System.out.println("as 32 bit unsigned: " + lg);
        int i = (int) lg;
        System.out.println("as 32 bit signed:   " + i);
        System.out.println("MAGIC = " + 0x61c88647);
    }
}

// as 32 bit unsigned: 2654435769
// as 32 bit signed:   -1640531527
// MAGIC = 1640531527
Copy the code

You can see that 0x61C88647 is associated with a magic number, which is (math.sqrt (5) -1)/2. This is the legendary golden ratio 0.618 (0.618 is just a rough number), which is 0x61C88647 = 2^32 * golden ratio. It also corresponds to the Fibonacci hashing method mentioned above.

4. Application scenarios of ThreadLocal

Usage scenarios of ThreadLocal:

  • Some objects can have problems when accessed concurrently by multiple threads, such as using the Parse () method of SimpleDataFormat, which has a Calendar object inside, and calling the Parse () method of SimpleDataFormat will call Calendar.clear() first, We then call calendar.add (). If one thread first calls add() and then another thread calls clear(), the parse() method will parse at the wrong time, and we can use ThreadLocal to solve the concurrent modification problem.
  • Another scenario is Spring transactions. Transactions are bound to threads. The Spring framework binds the current thread with a Jdbc Connection at the start of a transaction. The Spring framework uses ThreadLocal to implement this isolation:
public abstract class TransactionSynchronizationManager { / / thread binding resources, such as DataSourceTransactionManager binding: yes, it is a source of a Connection, in the process of the whole transaction execution / / all use the same Jdbc Connection private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources"); / / registered affairs synchronizer private static final ThreadLocal < Set < TransactionSynchronization > > synchronizations = new NamedThreadLocal<>("Transaction synchronizations"); Private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal<>("Current transaction name"); / / transaction read-only property private static final ThreadLocal < Boolean > currentTransactionReadOnly = new NamedThreadLocal < > (" Current transaction read-only status"); / / transaction isolation level private static final ThreadLocal < Integer > currentTransactionIsolationLevel = new NamedThreadLocal < > (" Current transaction isolation level"); Private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal<>("Actual Transaction" active"); }}Copy the code

But instead of using ThreadLocal for concurrency scenarios, you can use synchronized or locks to achieve thread-safety.