Two usage scenarios – the use of ThreadLocal

Typical scenario 1: Each thread needs a proprietary object (usually a utility class, typically using SimpleDateFormat and Random)

Typical scenario 2: Global variables need to be stored within each thread (such as retrieving user information in an interceptor), which can be used directly by different methods without the hassle of passing parameters.

Typical scenario 1: Each thread needs a proprietary object

Each Thread has its own instance copy, which is not shared.

Example: SimpleDateFormat. (When multiple threads share such a SimpleDateFormat, but the class is not safe)

  • Two threads using their own SimpleDateFormat, that’s fine;
  • And then 10, that’s 10 threads and 10 SimpleDateFormats, which is not elegant but acceptable
  • But when the requirement goes to 1000, you have to use thread pools and consume too much memory;
  • SimpleDateFormat = SimpleDateFormat = SimpleDateFormat = SimpleDateFormat = SimpleDateFormat = SimpleDateFormat = SimpleDateFormat; But still not elegant;
  • To solve this problem, assign a SimpleDateFormat to each thread, but this ThreadLocal is safe;
package threadlocal; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Using ThreadLocal, each thread is assigned its own dateFormat object to ensure thread-safety. Public class ThreadLocalNormalUsage05 {public static ExecutorService threadPool = Executors.newFixedThreadPool(10); public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 1000; i++) { int finalI = i; threadPool.submit(new Runnable() { @Override public void run() { String date = new ThreadLocalNormalUsage05().date(finalI); System.out.println(date); }}); } threadPool.shutdown(); } public String date(int seconds) {public String date(int seconds) {date date = new date(1000 * seconds); // SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); SimpleDateFormat dateFormat = ThreadSafeFormatter.dateFormatThreadLocal2.get(); return dateFormat.format(date); } } class ThreadSafeFormatter { public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() { @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); }}; public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal2 = ThreadLocal .withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); }Copy the code

Typical scenario 2: The current user information needs to be shared by all methods in the thread

  • A cumbersome solution would be to layer user as a parameter, from service-1() to service-2(), and so on, but this would result in redundant and unmaintainable code.
  • The advanced point is userMap, but when you have multiple threads working at the same time, you need to be thread-safe, you need to use synchronized, or concurrenthashmap, but whatever you use, it’s going to affect performance, right

Global variables need to be saved in each thread so that different methods can use them directly to avoid the hassle of passing parameters

  • Use ThreadLocal to hold some business memory (user permission information, usernames, userids, and so on retrieved from user systems)
  • This information is the same within 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 it as an argument
package threadlocal; /** * Public class ThreadLocalNormalUsage06 {public static void main(String[] args) {new Service1().process(""); }} Class Service1 {public void process(String name) {User User = new User(" super "); UserContextHolder.holder.set(user); new Service2().process(); } } class Service2 { public void process() { User user = UserContextHolder.holder.get(); ThreadSafeFormatter.dateFormatThreadLocal.get(); System.out.println("Service2 get username: "+ user.name); new Service3().process(); } } class Service3 { public void process() { User user = UserContextHolder.holder.get(); System.out.println("Service3 get username: "+ user.name); UserContextHolder.holder.remove(); } } class UserContextHolder { public static ThreadLocal<User> holder = new ThreadLocal<>(); } class User { String name; public User(String name) { this.name = name; }}Copy the code

Note:

  • The emphasis is on the sharing of different methods within the same request (within the same thread);
  • There is no need to override the initialValue() method, but the set() method must be called manually

ThreadLocal methods use a summary

Scenario 1: initialValue

The object is initialized on the first get of a ThreadLocal, and the timing of initialization can be controlled by us.

Scenario 2: Set

If we need to store objects in ThreadLocal, we can’t control when we generate them. For example, the user information generated by the interceptor is placed directly into a ThreadLocal using threadLocal. set.

ThreadLocal principle

Clarify Thread, ThreadLocalMap, and ThreadLocal

Introduction of main methods

  • T initialValue()Initialization:
  • void set(T t): sets a new value for this thread
  • T get(): gets the value of this thread. If it’s the first call to get(). Initialize is called to get this value
  • void remove(): Removes the value obtained by this thread

When a ThreadLocalMap conflict occurs, linear probing is used. In addition, welcome to pay attention to the public number Java notes shrimp, background reply “back-end interview”, send you an interview question treasure book!

ThreadLocal usage problem Memory leak

What is a memory leak

An object is no longer useful, but the memory used cannot be reclaimed.

The Value of the leak

  • Each Entry in ThreadLocalMap is a weak reference to a key, and each Entry contains a strong reference to a value.
  • Normally, when a thread terminates, a value stored in ThreadLocal is garbage collected because there are no strong references left.
  • However, if the thread does not terminate (for example, if the thread pool is kept for a long time), then the value corresponding to the key cannot be reclaimed. Thread – > ThreadLocalMap – > Entry (the key is Null) – > Value
  • Because there is a strong reference link between value and Thread, value cannot be reclaimed and OOM may occur. The JDK takes this into account, so the set,remove, and Rehash methods scan for a null key and set the value to NULL so that the value object can be reclaimed.
  • However, if a ThreadLocal is not used, then the set,remove, and rehash methods will not actually be called, and if the thread does not stop at the same time, the call chain will always exist, resulting in a memory leak of the value.
How do you avoid memory leaks?
  • Calling the remove method removes the corresponding Entry object to avoid memory leaks, so you should call the remove method after using ThreadLocal.