Application scenarios of ThreadLocal

In the usual business development, ThreadLocal has two typical usage scenarios

scene 1:

ThreadLocal is used to save each thread's exclusive object, creating a copy for each thread, so that each thread can modify its own copy without affecting the copy of other threads, ensuring thread safety.

Scene 2:

ThreadLocal is used as a scenario where information needs to be saved independently in each thread so that other methods can more easily obtain the information. The information obtained by each thread may be different. After the previously executed method saves the information, subsequent methods can be directly obtained through ThreadLocal, which avoids passing parameters, similar to the concept of global variables.

Typical scenario 1

This scenario is usually used to save thread-unsafe tool classes. The typical class that needs to be used is SimpleDateFormat .

In this case, each Thread has its own copy of the instance, and the copy can only be accessed and used by the current Thread, which is equivalent to the local variable inside each thread, which is also the meaning of ThreadLocal naming. Because each thread has its own copy rather than a common one, there is no problem of sharing among multiple threads.

For example, there are 10 threads that need to use SimpleDateFormat

public class ThreadLocalDemo01 {

    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                String data = new ThreadLocalDemo01().date(finalI);
                System.out.println(data);
            }).start();
            Thread.sleep(100);
        }

    }

    private String date(int seconds){
        Date date = new Date(1000 * seconds);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");
        return simpleDateFormat.format(date);
    }
}

We have created a SimpleDateFormat object for each thread, they do not affect each other, the code can be executed normally. Output result:

00:00
00:01
00:02
00:03
00:04
00:05
00:06
00:07
00:08
00:09

Let's take a look at this current state with a graph:

 

 

What if the SimpleDateFormat object is used by 1,000 threads?

We generally do not directly create so many threads, but through the thread pool, such as:

public class ThreadLocalDemo011 {
   public static ExecutorService threadPool = Executors.newFixedThreadPool(16);

    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            threadPool.submit(() -> {
                String data = new ThreadLocalDemo011().date(finalI);
                System.out.println(data);
            });
        }
        threadPool.shutdown();
    }

    private String date(int seconds){
        Date date = new Date(1000 * seconds);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");
        return simpleDateFormat.format(date);
    }

}

It can be seen that we used a 16-thread pool and submitted 1,000 tasks to this thread pool. It does the same thing in each task as before, or executes the date method, and creates one in this method

simpleDateFormat object. result:

00:00
00:07
00:04
00:02
...
16:29
16:28
16:27
16:26
16:39

What we have just done is to create a simpleDateFormat object for each task, that is, 1000 tasks correspond to 1000 simpleDateFormat objects, but what if the number of tasks is huge?

The creation of so many objects is costly, and the destruction after use is also costly, and it is also a waste of memory in memory.

We may think, should all threads share a simpleDateFormat object? But simpleDateFormat is not thread-safe, we must do synchronization, such as using synchronized to lock. Here may be our final solution. However, using synchronized locking will fall into a queued state, and multiple threads cannot work at the same time, so that the overall efficiency is greatly reduced. Is there a better solution?

Use ThreadLocal

For this kind of scenario, ThreadLocal is more appropriate. ThreadLocal maintains a simpleDateFormat object for each thread. This object is independent between threads and has nothing to do with each other. This also avoids thread safety issues. At the same time, the simpleDateFormat object will not create too much. There are only 16 threads in the thread pool, so 16 objects are needed.

public class ThreadLocalDemo04 {

    public static ExecutorService threadPool = Executors.newFixedThreadPool(16);

    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            threadPool.submit(() -> {
                String data = new ThreadLocalDemo04().date(finalI);
                System.out.println(data);
            });
        }
        threadPool.shutdown();
    }

    private String date(int seconds){
        Date date = new Date(1000 * seconds);
        SimpleDateFormat dateFormat = ThreadSafeFormater.dateFormatThreadLocal.get();
        return dateFormat.format(date);
    }
}

class ThreadSafeFormater{
    public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("mm:ss"));
}

Let's take a look at this current state with a graph:

 

 

Typical scenario 2

Each thread needs to save information similar to global variables (such as user information obtained in the interceptor), which can be used directly by different methods to avoid the trouble of passing parameters but do not want to be shared by multiple threads (because users obtained by different threads The information is different).

For example, use ThreadLocal to save some business content (user permission information, user name obtained from the user system, user ID, etc.), the information is the same in the same thread, but different threads use different business content.

In the thread life cycle, all the objects that have been set by the static ThreadLocal instance get () method are obtained to avoid the trouble of passing this object (such as the user object) as a parameter.

For example, if we are a user system, when a request comes in, a thread will be responsible for executing the request, and then the request will call service-1 (), service-2 (), service-3 (), service -4 (), these 4 methods may be distributed in different classes.

 

We give an example in the form of pictures:

 

 

 

Code:

package com.kong.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 value to ThreadLocal, subsequent services can be obtained directly through ThreadLocal. 
        UserContextHolder.holder.set (user);
         new Service2 (). Service2 (); 
    } 
} 

class Service2 {
     public  voidservice2 () { 
        User user = UserContextHolder.holder.get (); 
        System.out.println ( "User that service2 got:" + user.name);
         new Service3 (). service3 (); 
    } 
} 

class Service3 {
     public  void service3 () { 
        User user = UserContextHolder.holder.get (); 
        System.out.println ( "User that service3 got:" + user.name);
         // After the entire process is executed, you must execute remove 
        UserContextHolder .holder.remove (); 
    } 
} 

class UserContextHolder {
     // Create ThreadLocal to save User object
    public static ThreadLocal<User> holder = new ThreadLocal<>();
}

class User {
    String name;
    public User(String name){
        this.name = name;
    }
}

 

The results of the implementation:

Service2 user: jack 
Service3 user: jack

 

Guess you like

Origin www.cnblogs.com/zz-ksw/p/12684877.html