explain:
(1) Content of this blog: introduce the first usage scenario of ThreadLocal;
(2) ThreadLocal is the first use scenario, which is usually used when calling tool classes through multiple threads;
(3) Statement: this blog has an evolutionary relationship. It's like reading a story from the beginning to the end;
catalogue
1: The first usage scenario of ThreadLocal: scenario introduction;
2: The first usage scenario of ThreadLocal: demonstration;
1. Initial situation: there are two tasks, and we create two threads to process them;
(2) Create ThreadLocalNormalUsage00 class to demonstrate;
2. The second case: there are 30 tasks, and we create 30 threads to process them;
(2) Create ThreadLocalNormalUsage01 class to demonstrate;
3. The third case: there are 1000 tasks, which we use thread pool to process;
(2) Create ThreadLocalNormalUsage02 class to demonstrate;
(2) Create ThreadLocalNormalUsage03 class to demonstrate;
(2) Create ThreadLocalNormalUsage04 class to demonstrate;
(2) Create ThreadLocalNormalUsage05 class to demonstrate;
1: The first usage scenario of ThreadLocal: scenario introduction;
(1) Let's take a look at a case. We have a class that can complete a certain function;
● [for example, we define a tool class dateformat class for processing dates;] Base note, suppose that there are 10000 dates to be processed now, naturally we need to instantiate the DateFormat class and then call the method defined in that class to process the date; → [at the same time, because there are 10000 tasks to be processed, multithreading can naturally be used to improve the processing efficiency;] → [suppose we create a thread pool with 10 core threads and 10 maximum threads to help handle these 10000 tasks] → [then, it can be considered that when the thread pool processes these 10000 tasks, 10 threads will run at the same time;] → [then, in order to ensure that the data is not disordered, the 10 threads running at the same time need to instantiate their own dateformat objects] → [that is, since we have 10 threads running at the same time, we will create 10 SimpleDateFormat objects, one for each thread;];
● using this case can help understand the meaning of the first usage scenario of ThreadLocal;
(2) The first usage scenario of ThreadLocal: each Thread class has its own instance copy; At the same time, instances between threads are not shared;
● combined with the above cases, this sentence is easy to understand;
● in fact, we can also feel the meaning of Local through the naming of ThreadLocal. The meaning of Local is a bit [the instance of this thread can only be used by the current thread];
Here, we use the evolutionary path of SimpleDateFormat to iterate step by step to illustrate the usefulness of ThreadLocal in the first scenario;
2: The first usage scenario of ThreadLocal: demonstration;
1. Initial situation: there are two tasks, and we create two threads to process them;
(1) Briefing;
● we want to replace the two dates with the dates in the corresponding format; Then, we use two threads to handle these two tasks;
(2) Create ThreadLocalNormalUsage00 class to demonstrate;
ThreadLocalNormalUsage00 class:
package threadLocal; import java.text.SimpleDateFormat; import java.util.Date; public class ThreadLocalNormalUsage00 { public String date(int seconds) { //The unit of seconds is seconds, while the parameter of Date() is required to be milliseconds; So it's 1000 here; //[date returned] = [1970.1.1 00:00:00] + [1000*seconds]; (PS: if it's in the East eighth District, it's [1970.1.1 08:00:00]) Date date = new Date(1000 * seconds); //Define time display format SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); return dateFormat.format(date); } //When there are only two tasks, there seems to be no problem at present; Everything is OK; public static void main(String[] args) { //First thread new Thread(new Runnable() { @Override public void run() { String date = new ThreadLocalNormalUsage00().date(10); System.out.println(date); } }).start(); //Second thread new Thread(new Runnable() { @Override public void run() { String date = new ThreadLocalNormalUsage00().date(104707); System.out.println(date); } }).start(); } }
explain:
(1) Class content description;
2. The second case: there are 30 tasks, and we create 30 threads to process them;
(1) Briefing;
Still follow the above idea. If there are 30 tasks, we will create 30 threads to process them;
(2) Create ThreadLocalNormalUsage01 class to demonstrate;
package threadLocal; import java.text.SimpleDateFormat; import java.util.Date; public class ThreadLocalNormalUsage01 { public String date(int seconds) { //The unit of seconds is seconds, while the parameter of Date() is required to be milliseconds; So it's 1000 here; //[date returned] = [1970.1.1 00:00:00] + [1000*seconds]; (PS: if it's in the East eighth District, it's [1970.1.1 08:00:00]) Date date = new Date(1000 * seconds); //Define time display format SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); return dateFormat.format(date); } //There is a disadvantage here. With 30 tasks, we have to create 30 threads; So if there are 10000 tasks, do we have to create 10000 threads? such // It will bear the cost of [creating and destroying 10000 threads]; //Therefore, it is thought that [thread pool] can be used to decouple [thread life cycle] from [thread execution content]; public static void main(String[] args) throws InterruptedException { for (int i = 0; i <30 ; i++) { int finalI = i; new Thread(new Runnable() { @Override public void run() { String date = new ThreadLocalNormalUsage01().date(finalI); System.out.println(date); } }).start(); Thread.sleep(100); } } }
explain:
(1) Class content description;
(2) Additional note: the loop variable of the for loop cannot be modified within the loop, so finalI is used to deal with it;
3. The third case: there are 1000 tasks, which we use thread pool to process;
(1) Briefing;
● if we have 1000 tasks, obviously we can't cycle 1000 times and create 1000 threads to process 1000 tasks;
● this is very bad. If we create 1000 threads, we need to bear the cost of creating and destroying 1000 threads;
● it's easy to think that we can use thread pool to help us decouple [thread life cycle] from [tasks performed by threads]; In this way, in the face of 1000 tasks, we don't have to create 1000 threads foolishly;
(2) Create ThreadLocalNormalUsage02 class to demonstrate;
package threadLocal; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadLocalNormalUsage02 { public String date(int seconds) { //The unit of seconds is seconds, while the parameter of Date() is required to be milliseconds; So it's 1000 here; //[date returned] = [1970.1.1 00:00:00] + [1000*seconds]; (PS: if it's in the East eighth District, it's [1970.1.1 08:00:00]) Date date = new Date(1000 * seconds); //Define time display format SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); return dateFormat.format(date); } //Create a thread pool public static ExecutorService threadPool = Executors.newFixedThreadPool(10); //(1) However, there is a disadvantage here: for each of the 1000 tasks, we have to call the date() method once; And every time you call the date() side // Method, a SimpleDateFormat object will be created; Therefore, in order to complete 1000 tasks, we need to create 1000 tasks // A SimpleDateFormat object; That is, we need to create and destroy SimpleDateFormat 1000 times; //(2) From this, I thought: since each task needs to use the SimpleDateFormat object, why not create the SimpleDateFormat pair more than once // For example, all the 1000 tasks use the same SimpleDateFormat object, isn't it? public static void main(String[] args) throws InterruptedException { for (int i = 0; i <1000 ; i++) { int finalI = i; //Then, put the task into the thread pool; threadPool.submit(new Thread(new Runnable() { @Override public void run() { String date = new ThreadLocalNormalUsage02().date(finalI); System.out.println(date); } })); } threadPool.shutdown();//Finally, remember to turn off the thread pool } }
explain:
(1) Class content description;
(2) That is, this method has a disadvantage: [we need to execute 1000 tasks] → [each task will create a SimpleDateFormat object] → [then, in the face of 1000 tasks, we need to create and destroy the SimpleDateFormat object 1000 times; this overhead is very large;]
4. The fourth case: there are 1000 tasks, which we use thread pool to process; At the same time, let these 1000 tasks share the same SimpleDateFormat object;
(1) Briefing;
● we use thread pool to process 1000 tasks;
● at the same time, in order to reduce the cost of creating and destroying SimpleDateFormat objects 1000 times, we let 1000 tasks share the same SimpleDateFormat object;
(2) Create ThreadLocalNormalUsage03 class to demonstrate;
package threadLocal; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadLocalNormalUsage03 { public String date(int seconds) { //The unit of seconds is seconds, while the parameter of Date() is required to be milliseconds; So it's 1000 here; //[date returned] = [1970.1.1 00:00:00] + [1000*seconds]; (PS: if it's in the East eighth District, it's [1970.1.1 08:00:00]) Date date = new Date(1000 * seconds); return dateFormat.format(date); } //Define time display format static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); //Create a thread pool public static ExecutorService threadPool = Executors.newFixedThreadPool(10); //Because all threads share the same SimpleDateFormat object (to process numbers and get date), thread safety problems will occur; //Therefore, I wonder if it is possible to use the same resource for different threads by locking // Thread safety problem when the source (the common resource here is the SimpleDateFormat object)?] public static void main(String[] args) throws InterruptedException { for (int i = 0; i <1000 ; i++) { int finalI = i; //Then, put the task into the thread pool; threadPool.submit(new Thread(new Runnable() { @Override public void run() { String date = new ThreadLocalNormalUsage03().date(finalI); System.out.println(date); } })); } threadPool.shutdown();//Finally, remember to turn off the thread pool } }
explain:
(1) Class content description;
(2) That is, there is a problem with this method: since 10 threads share the same SimpleDateFormat object, this will lead to thread safety problems;
5. The fifth case: there are 1000 tasks, which we use thread pool to process; At the same time, let these 1000 tasks share the same SimpleDateFormat object; At the same time, lock the statement block calling the SimpleDateFormat method and use the synchronized keyword;
(1) Briefing;
● since in the fourth case, there are 10 core threads in the thread pool that use the SimpleDateFormat object "at the same time", which leads to the problem of thread safety;
● in order to ensure that only one thread can use the SimpleDateFormat object at the same time, it is natural to think of using the synchronized keyword;
(2) Create ThreadLocalNormalUsage04 class to demonstrate;
package threadLocal; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadLocalNormalUsage04 { public String date(int seconds) { //The unit of seconds is seconds, while the parameter of Date() is required to be milliseconds; So it's 1000 here; //[date returned] = [1970.1.1 00:00:00] + [1000*seconds]; (PS: if it's in the East eighth District, it's [1970.1.1 08:00:00]) Date date = new Date(1000 * seconds); String s = null; synchronized (ThreadLocalNormalUsage04.class) { s = dateFormat.format(date); } return s; } //Define time display format static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); //Create a thread pool public static ExecutorService threadPool = Executors.newFixedThreadPool(10); //Here we use synchronized to solve the thread safety problem; However, synchronized will also lead to performance degradation; public static void main(String[] args) throws InterruptedException { for (int i = 0; i <1000 ; i++) { int finalI = i; //Then, put the task into the thread pool; threadPool.submit(new Thread(new Runnable() { @Override public void run() { String date = new ThreadLocalNormalUsage04().date(finalI); System.out.println(date); } })); } threadPool.shutdown();//Finally, remember to turn off the thread pool } }
explain:
(1) Class content description;
(2) Problem description;
Using [synchronized keyword can ensure that only one thread can use the SimpleDateFormat object at the same time; admittedly, this can avoid thread safety problems] → [we used the thread pool to create 10 core threads. We originally hoped that we could have 10 threads to process 1000 tasks at the same time, so as to speed up the processing speed of 1000 tasks] → [however, if we use the synchronized keyword, only one thread can use the SimpleDateFormat object at the same time; this will not give full play to the advantage of "10 threads can process tasks at the same time, so as to improve the speed of processing 1000 tasks";
(3) Additional instructions;
The synchronized keyword can be used on the statement block. If necessary, you can refer to it[ Java thread 4: thread synchronization];
(4) For this reason, in order to not only give play to the advantage of the thread pool, "10 threads can handle tasks concurrently", but also prevent different threads from sharing the same SimpleDateFormat object at the same time, resulting in thread safety problems; The first application scenario of ThreadLocal comes out;
6. The sixth case: there are 1000 tasks, which we use thread pool to process; At the same time, use ThreadLocal;
(1) Briefing;
● at present, our requirements are: we want to use the thread pool (for example, the thread pool here is set to 10 for the number of core threads and the maximum number of threads) to process 1000 tasks, and we can have 10 threads to process tasks concurrently at the same time; I also want to avoid these 10 threads using the same SimpleDateFormat object at the same time, which leads to thread safety problems;
● after a little thought, we can find out whether this is possible: we let these 10 threads have a SimpleDateFormat object respectively (that is, because there will be 10 threads processing tasks concurrently in the thread pool, we will create 10 SimpleDateFormat objects, one for each thread); This situation is similar to that, on average, each thread will process 100 tasks. When processing these 100 tasks, the SimpleDateFormat object held by the thread is always the same;;;;;; In this way, it can not only allow 10 threads to process concurrently, but also avoid different threads sharing the same SimpleDateFormat object at the same time;
● this idea is the first use scenario of ThreadLocal;
(2) Create ThreadLocalNormalUsage05 class to demonstrate;
First, create the ThreadSafeFormatter class, which is mainly used as the source of the SimpleDateFormat object;
package threadLocal; import java.text.SimpleDateFormat; public class ThreadSafeFormatter { public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() { @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); } }; }
explain:
(1) When we created the SimpleDateFormat object earlier, it came out of new, which is not good;
(2) ThreadSafeFormatter class can be regarded as a tool class. The main function of this class is to produce thread safe SimpleDateFormat objects;
Then, create the ThreadLocalNormalUsage05 class, which can use the ThreadSafeFormatter class above to obtain the thread safe SimpleDateFormat object;
package threadLocal; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadLocalNormalUsage05 { public String date(int seconds) { //The unit of seconds is seconds, while the parameter of Date() is required to be milliseconds; So it's 1000 here; //[date returned] = [1970.1.1 00:00:00] + [1000*seconds]; (PS: if it's in the East eighth District, it's [1970.1.1 08:00:00]) Date date = new Date(1000 * seconds); //Define time display format // SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); SimpleDateFormat dateFormat = ThreadSafeFormatter.dateFormatThreadLocal.get(); // SimpleDateFormat dateFormat = ThreadSafeFormatterWithLambda.dateFormatThreadLocal.get(); return dateFormat.format(date); } //Create a thread pool 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; //Then, put the task into the thread pool; threadPool.submit(new Thread(new Runnable() { @Override public void run() { String date = new ThreadLocalNormalUsage05().date(finalI); System.out.println(date); } })); } threadPool.shutdown();//Finally, remember to turn off the thread pool } }
explain:
(1) Class content description;
(2) At this time, since there is no case that different threads share the same object at the same time, the thread safety problem will not occur;
(3) In addition, with the help of Lambda expression, the process of ThreadLocal production object is simplified;
Create a ThreadSafeFormatterWithLambda class. The main function of this class is to use Lambda expressions to simplify the process of producing ThreadLocal objects;
package threadLocal; import java.text.SimpleDateFormat; public class ThreadSafeFormatterWithLambda { public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd hh:mm:ss")); }
explain:
(1) Class content description;
(2) In ThreadLocalNormalUsage05, the same method is used to use ThreadSafeFormatterWithLambda;
(3) For the contents of Lambda expression, please refer to it if necessary[ (10) Front of frame: maven, factory mode, reflection, Lambda; ]Contents in the column;