ThreadLocal

Last week, THE author participated in the interview of business engineer of Weimeng New Retail, and got several offers. Finally, I chose a listed state-owned enterprise, 955 does not clock in, and the provident fund is full. The following are some problems I met in the interview, and I think it is necessary to discuss the details in depth.

Interviewer: See what ThreadLocal is used in your project and how to implement it (tell me more about it)? Me (smiling) : ThreadLocal is not designed to solve the problem of multithreaded access to shared objects. In general, objects passed to a thread through threadlocale.set () are used by the thread itself, and no other thread needs to access them. I can't access it. Different objects are accessed in each thread.Copy the code

Interviewer: Tell me about the variables in the ThreadLocal class

I:

  private final int threadLocalHashCode = nextHashCode();
  private static final int HASH_INCREMENT = 0x61c88647;
  private static AtomicInteger nextHashCode =     new AtomicInteger();
Copy the code

Difference between continuously generated hash codes – Converts implicit sequential thread local ids into approximately optimally distributed multiplicative hash values to obtain a power of two size tables.

Look at the definitions in the official documentation

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-pour in less common cases ThreadLocals relies on a per-thread linear probe hash map attached to each Thread (Thread. ThreadLocals and Inheritable ThreadLocals). When using a ThreadLocal object as a key, search through ThreadLocal ashCode. This is a custom hash code (useful only in ThreadLocalMaps) that eliminates collisions in the common case of continuously constructed ThreadLocals for the same thread, while maintaining stable behavior in extreme cases. Interviewer: The guy can do that. Can you tell me how he did it? Me: The ThreadLocal class provides thread-local variables. These variables differ from their normal counterparts because each thread that accesses one (through its GET or set methods) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in a class that you want to associate state with a thread (for example, a user ID or transaction ID). For example, the following class generates unique identifiers that are local to each thread. The id of the thread is assigned on the first call to threadid.get () and remains the same in subsequent calls. Import the Java. Util. Concurrent. Atomic. AtomicInteger; Thread-local variable private static final ThreadLocal<Integer> threadId = new thread-local <Integer> () {@override protected Integer initialValue() {return nextId.getAndIncrement(); }}; // Returns the unique ID of the current thread, assigning it public static int get() if necessary {return threadid.get (); }Copy the code

Interviewer: Tell me more about the difference between ThreadLocal and Synchronized

Me: ThreadLocal<T> is actually a variable bound to the thread. ThreadLocal and Synchonized are both used to deal with multi-threaded concurrent access. However, there are essential differences between synchronized and ThreadLocal: 1. Synchronized is used for data sharing between threads, while ThreadLocal is used for data isolation between threads. 2. Synchronized is a mechanism that uses locks to make variables or code blocks accessible only to one thread at a time. ThreadLo CAL provides each thread with a copy of a variable, so that each thread is not accessing the same object at any one time, thus isolating multiple threads from sharing data. Synchronized, on the other hand, is used to obtain data sharing when communicating between multiple threads. A ThreadLocal stores items to its Map, and the ThreadLocal then attaches the Map to the current thread, so that the Map belongs only to that thread.Copy the code

Interviewer: Talk about how ThreadLocal works

To see the principle then have to look from the source.

Public void set(T) {public void set(T); If threadLocalMap is not empty, update the value of the variable to save, otherwise create threadLocalMap. ThreadLocalMap map = getMap(t); if (map ! = null) map.set(this, value); Else // Initialize thradLocalMap and assign createMap(t, value); }} If a ThreadLocal set is assigned to the current thread, the ThreadLocalMap attribute of the thread will be acquired. If the map attribute is not empty, the value is updated directly, and if the map is empty, threadLocalMap is instantiated and the value is initialized. ```jsCopy the code

So what is ThreadLocalMap, and what createMap does, let’s keep reading. You finally own idea with the source code, there will be a deeper understanding.

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; }}} ThreadLocalMap is an internal static class of ThreadLocal, which consists mainly of entries to hold data, and is an inherited weak reference. Use ThreadLocal as the key inside the Entry and use the value we set as the value. Void createMap(Thread t, t firstValue) {t.htreadlocals = new ThreadLocalMap(this, firstValue); void createMap(Thread t, t firstValue) {t.htreadlocals = new ThreadLocalMap(this, firstValue); } //ThreadLocalMap constructor ThreadLocalMap(ThreadLocal<? > firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); } ThreadLocal public T get() {ThreadLocal T = thread.currentThread (); ThreadLocalMap map = getMap(t); // if (map! Threadlocalmap. Entry e = map.getentry (this); if (e ! = null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; Return setInitialValue(); TheralLocalMap (threadLocal); } 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; } public void remove() {ThreadLocalMap m = getMap(thread.currentThread ()); if (m ! = null) m.remove(this); } remove directly removes the value of ThrealLocal from ThreadLocalMap of the current offset Thread. Why delete? It involves memory leak. In fact, the key used in ThreadLocalMap is a weak reference to ThreadLocal. The weak reference is that if the object has only weak references, it will be cleaned up in the next garbage collection. So if a ThreadLocal is not strongly referenced, it will be cleaned up during garbage collection, and so will any key in the ThreadLocalMap that uses this ThreadLocal. However, a value is a strong reference and will not be cleaned up, resulting in a value with a null key. A ThreadLocal is actually a variable bound to the thread, so there is a problem: if a variable in a ThreadLocal is not removed or replaced, its life will coexist with that of the thread. Thread management in thread pools is usually managed by thread reuse, where threads are difficult to terminate or never terminate, meaning that the duration of threads is unpredictable and even consistent with the lifetime of the JVM. For example, if a ThreadLocal directly or indirectly wraps collection classes or complex objects, then the space occupied by the inner collection classes and complex objects may start to grow each time the objects are fetched from the same ThreadLocal and then manipulated.Copy the code

Interviewer: Common usage scenarios of ThreadLocal

I:

For the first point, each thread has its own instance, which can be implemented in many ways. For example, you can build a separate instance inside a thread. ThreadLocal can fulfill this requirement in a very convenient form. The second point can be implemented by passing references between methods as long as the first point is satisfied (each thread has its own instance). ThreadLocal makes code less coupled and implementation more elegant. Private static final ThreadLocal threadSession = new ThreadLocal(); private static final ThreadLocal threadSession = new ThreadLocal(); public static Session getSession() throws InfrastructureException { Session s = (Session) threadSession.get(); try { if (s == null) { s = getSessionFactory().openSession(); threadSession.set(s); } } catch (HibernateException ex) { throw new InfrastructureException(ex); } return s; Each thread needs to store information similar to global variables (such as user information obtained in the interceptor), which can be directly used by different methods. Avoid the hassle of passing parameters but don't want to be shared by multiple threads (because different threads get different user information). For example, ThreadLocal holds business content (user permission information, user names obtained from user systems, user ids, and so on) that is the same in the same thread, but different threads use different business content. The static ThreadLocal instance gets its set object through its get() method throughout the thread's life cycle, avoiding the hassle of passing that object (such as the User object) as an argument. For example, if we are a user system, when a request comes in, a thread is responsible for executing the request, which then calls service-1(), service-2(), service-3(), and service-4() in sequence. These four methods may be distributed in different classes. This example is similar to storing sessions. package com.Jason.threadlocal; public class ThreadLocalDemo05 { public static void main(String[] args) { User user = new User("jack"); new Service1().service1(user); }} class Service1 {public void Service1 (User User){// Assign ThreadLocal to ThreadLocal. UserContextHolder.holder.set(user); new Service2().service2(); } } class Service2 { public void service2(){ User user = UserContextHolder.holder.get(); System.out.println("service2 gets user :"+user.name); new Service3().service3(); } } class Service3 { public void service3(){ User user = UserContextHolder.holder.get(); System.out.println("service3 gets user :"+user.name); / / in the whole process execution has been completed, you must perform the remove UserContextHolder. Holder. The remove (); }} class UserContextHolder {public static ThreadLocal<User> holder = new ThreadLocal<>(); } class User { String name; public User(String name){ this.name = name; }} Result of execution: We know that in general, only stateless beans can be shared in a multithreaded environment. In Spring, Most beans can be declared singleton scoped. Because the Spring for some Bean (such as RequestContextHolder, TransactionSynchronizationManager, LocaleContextHolder, etc.) Non-thread-safe "stateful objects" are encapsulated with ThreadLocal to make them thread-safe "stateful objects" as well, so stateful beans can work singleton style in multithreading. Generally, Web applications are divided into three layers: the presentation layer, the service layer and the persistence layer. The corresponding logic is written in different layers, and the lower layer opens function calls to the upper layer through interfaces. In general, all program calls from receiving a request to returning a response belong to the same thread, as shown in the figure.Copy the code

This allows users to store non-thread-safe variables in ThreadLocal as needed, and all objects in the calling thread that responds to the same request access the same ThreadLocal variable that is bound to the current thread. Listing 9-5 TopicDao: Threadsafe Public class TopicDao {//① a threadsafe variable private Connection conn; Public void addTopic(){// Statement stat = conn.createstatement (); ... } Since conn at ① is a member variable, and since the addTopic() method is non-thread-safe, a new TopicDao instance (non-singleton) must be created when used. Listing 9-6 TopicDao: Thread-safe import java.sql.Connection; import java.sql.Statement; Public class TopicDao {//① Use ThreadLocal to save Connection variables private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>(); Public static Connection getConnection(){// if connThreadLocal does not have a Connection for this thread, create a new Connection and store it in a thread local variable. if (connThreadLocal.get() == null) { Connection conn = ConnectionManager.getConnection(); connThreadLocal.set(conn); return conn; }else{//③ return connThreadlocal.get (); }} public void addTopic() {statementstat = getConnection().createstatement (); } When TopicDao is used by different threads, check whether connThreadlocal.get () is null. If null, it indicates that the current thread does not have a Connection object. If not null, the current thread already owns the Connection object and can use it directly. This ensures that different threads use thread-specific connections and not other threads' connections. Therefore, the TopicDao can be singleton shared. However, it is not possible to share the same Connection with other DAOs. To have multiple DAOs in the same transaction share the same Connection, you must store the Connection in a common external class using ThreadLocal. But this example basically illustrates Spring's approach to making stateful classes thread-safe. Later in this chapter, we'll explain in detail how Spring solves the problem of transaction management with ThreadLocal.Copy the code