ThreadLocal

ThreadLocal is a thread’s internal storage class that can store data within a specified thread, and only the specified thread can get the stored data.

Each Thread object contains a member variable, threadLocals, of type ThreadLocalMap, which stores all ThreadLocal objects and their values in 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).
 */
 /** * This class provides thread-local variables. These variables differ from the normal correspondence of * in that each thread accessing a thread (via the *{@ code fetch} or {@ code set} methods) has its own, independently initialized copy of the * variable. {@codeThreadLocal} instances are typically private * static fields in classes that want to associate state with threads (e.g. , * user ID or transaction ID). * /
 
Copy the code

Each thread has an instance of TheadLocalMap, which is managed by ThreadLocal. Each new thread instantiates a ThreadLocalMap and assigns it to the member variable ThreadLocals. If threadLocals already exists, the existing object is used. (Lazy loading)

/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

Copy the code
Set (), get() will worknullWorth cleaning up, a call to expungeStaleEntry() remove() disconnects the weak reference before garbage collection, and then sets the key tonullValue to clearCopy the code
// The name of the map is threadLocals
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
}


public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! =null)
            map.set(this, value);
        else
            createMap(t, value);
}

//private final static ThreadLocal<SecurityContext> CONTEXT_HOLDER = new ThreadLocal<>();
public T get(a) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! =null) {
            //this: CONTEXT_HOLDER
            ThreadLocalMap.Entry e = map.getEntry(this);
            if(e ! =null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                returnresult; }}return setInitialValue();
    }
Copy the code

Application scenarios

Consider ThreadLocal when some data is thread-scoped and different threads have different copies of the data.

Stateless, high concurrency scenarios where independent replica variables do not affect business logic. If the business logic is heavily dependent on replica variables, ThreadLocal is not an appropriate solution.

SET and GET are implemented by calling SET () of ThreadLocalMap

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! =null)
            map.set(this, value);
        else
            createMap(t, value);
    }
/ / getMap method
ThreadLocalMap getMap(Thread t) {
      // Thred maintains a ThreadLocalMap
      return t.threadLocals;
 }

//createMap
void createMap(Thread t, T firstValue) {
      // Instantiate a new ThreadLocalMap and assign it to the thread member variable threadLocals
      t.threadLocals = new ThreadLocalMap(this, firstValue);
}

Copy the code

ThreadLocalMap

ThreadLocalMap maintains an array table for each Thread, and ThreadLocal determines an array subscript that corresponds to the location of the value store.

For a ThreadLocal, its index value I is fixed. For different threads, the same ThreadLocal corresponds to different subscripts of table, that is, table[I]. Tables of different threads are independent of each other.

Thread isolation feature

  • Synchronized resolves access conflicts by sacrificing time through thread waiting
  • ThreadLocal resolves conflicts by giving each thread a separate piece of storage, sacrificing space

Memory leak problem

There is a memory leak that must be removed by calling remove() each time ThreadLocal is used.

Entry inherits from WeakReference<ThreadLocal<? >>, an Enrty consists of ThreadLocal objects and objects. The key of the Entry is a ThreadLocal object and is a weak reference. When there is no strong reference to a key, the key is collected by the garbage collection period.

How do I avoid a ThreadLocal memory leak

Memory leaks occur when objects or variables that are no longer being used cannot be reclaimed.

Strong references: With the most common reference (new), an object has a strong reference that is not collected by the garbage collector. When running out of memory, the Java virtual machine would rather throw an OOM exception than reclaim this object.

Weak references: When the JVM does garbage collection, it reclaims objects associated with weak references regardless of whether memory is sufficient. In Java, using Java. Lang. Ref. The WeakReference class. You can use weak references in the cache.

The thread stack holds a reference to ThreadLocal and a reference to CurrentThreadRef. ThreadLocal is a strong reference. When the strong reference disappears, the weak reference in the ThreadLocalMap (key — ThreadLocal) will disappear. The key will disappear, but the value will still be referenced until the thread exits. The value of value is cleared.

Weak references are not resolved until the thread exits.

Any of the set (), get (), or remove () methods in the next ThreadLocalMap will cause the value to be cleared

Correct use method

  • ThreadLocal calls its remove () method to clean up data each time it is used
  • Define a ThreadLocal variable as static, so that a strong reference to a ThreadLocal will always exist, rather than a strong reference to a normal new that may become null. This ensures that the value of an Entry’s value can be accessed at any time through a weak reference to ThreadLocal and then cleaned.

Practical use of Halo

Interface socket

@NonNull
    public static SecurityContext getContext(a) {
        // Get from thread local
        SecurityContext context = CONTEXT_HOLDER.get();
        if (context == null) {
            // If no context is available now then create an empty context
            context = createEmptyContext();
            // Set to thread local
            CONTEXT_HOLDER.set(context);
        }

        return context;
    }


public static void setContext(@Nullable SecurityContext context) {
        CONTEXT_HOLDER.set(context);
    }
Copy the code
// Check user login status and set this field
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if(authentication ! =null) {
            // Blogger comment
            User user = authentication.getDetail().getUser();
            commentParam.setAuthor(StringUtils.isBlank(user.getNickname()) ? user.getUsername() : user.getNickname());
            commentParam.setEmail(user.getEmail());
            commentParam.setAuthorUrl(optionService.getByPropertyOrDefault(BlogProperties.BLOG_URL, String.class, null));
        }

        // Validate the comment param manually
        ValidationUtils.validate(commentParam);
Copy the code
// Get the user
        User user = userService.getById(optionalUserId.get());

        // Build user detail
        UserDetail userDetail = new UserDetail(user);

        // Set security
        SecurityContextHolder.setContext(new SecurityContextImpl(new AuthenticationImpl(userDetail)));
Copy the code