This is the 8th day of my participation in the More text Challenge. For details, see more text Challenge
Author: JavaGieGie
Wechat official account: Java Development zero to one
preface
The multi-threaded series of Squat pit has been updated in the previous six chapters, it is strongly recommended that friends follow the order of learning:
“Squat pit can also enter the factory” multithreaded series of articles directory
I: the dog is left son, good morning, two days see, still think your job-hopping entered a 3 company!
Dog leftover son: you this is to eat chili farting, bring excitant of, spend Gie tutorial not to see finish, I jump what slot.
I: Yo hoo, that you these two days go where?
The dog left son: THIS is not seclusion practice, just get out of quarantine!
I:… We’re done. It’s a stupid dog.
The body of the
Me: Doggie, can you tell me what a ThreadLocal is? Last time a xiang asked me, I did not answer up, very embarrassed?
Don’t be afraid. The dog will teach you.
ThreadLocal is primarily used to provide a thread-local variable, which is visible only to the current thread.
ThreadLocal maintains a data structure (similar to HashMap) internally, and when a variable is needed, a copy of the variable is created in each thread, which is then manipulated by set() and get(), thereby isolating the data between threads.
Me: That sounds not too hard! So where do we use ThreadLocal?
Although it sounds very simple, but to really master, it is not so simple. Pour a cup of tea, point to praise, and listen to the old ak fine way, the following cite a few commonly used cases.
1) SimpleDateFormat
You can be sure that 100% of you have used SimpleDateFormat to format time. We usually encapsulate a utility class, such as the following example, to print 1 to 1000 seconds of formatting time.
public class ThreadLocalDemo {
// Create a fixed size thread pool
public static ExecutorService executorService = Executors.newFixedThreadPool(5);
public static void main(String[] args) {
for (int i = 0; i <= 1000; i++) {
int time = i ;
executorService.submit(new Runnable() {
@Override
public void run(a) {
// Call the time class static methodString formatTime = TimeUtil.getSecond(time); System.out.println(formatTime); }}); } executorService.shutdown(); }}// Time utility class
class TimeUtil {
static SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-DD HH:mm:ss");
public static String getSecond(int count){
Date date = new Date(1000*count);
returnsdf.format(date); }}Copy the code
Submit: add thread task; Shutdown: stops the thread pool.
If there are too many tasks to be processed, they will be queued and continue to execute after the current task has finished.
At this point, we will find a strange scene, the time variable is obviously a natural number from 0 to 1000, and there is no repeat of the number, here why print the same formatted result. Think of those methods written before, don’t feel cold back it.
The reason for this is that all threads share the same simpleDateFormat object, which makes it thread-safe, and the clever ones must have figured out that we could use synchronized to lock key code and make the result thread-safe.
public static String getSecond(int count){
Date date = new Date(1000*count);
String result = null;
synchronized(ThreadLocalDemo.class){
result = sdf.format(date);
}
return result;
}
Copy the code
This works for our purposes, and there are no surprises in the results, but it can cause only one thread at a time to perform the time formatting (performing serialization), which can seriously affect the performance of the program and cannot be tolerate under high concurrency conditions.
This is where our main character, ThreadLocal, comes in. Let’s look directly at the code here.
class TimeUtil {
public static String getSecond(int count){
Date date = new Date(1000*count);
// Use get to get SimpleDateFormat
SimpleDateFormat sdf = ThreadLocalUtils.simpleDateFormatThreadLocal.get();
returnsdf.format(date); }}/ / create a ThreadLocal
class ThreadLocalUtils {
public static ThreadLocal<SimpleDateFormat> simpleDateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue(a) {
return new SimpleDateFormat("YYYY-MM-DD HH:mm:ss"); }}; }Copy the code
No matter how you execute it, the result is never the same, indicating that objects created with ThreaLocal are guaranteed to be threaded.
2) Avoid passing some parameters
Yesterday 618 do not know everyone have chop hands, anyway I have no hands can chop, just want to live well. Here we think about a question, when you place an order, what is the background process? In fact, the background program needs to handle many processes including user information query, coupon query, shipping address query, message notification, and so on. Since user information is likely to be used in each step, passing user information as a parameter leads to code that is highly coupled and bloated, which is very difficult to maintain.
Some of you might be thinking, instead of using your ThreadLocal, I’ll just write a static map set and save it.
In the case of multiple threads accessing the same variable, we know that this can cause thread-safety problems, and that using a collection of thread-safe types (such as ConCurrentHashMap) or locking them directly can affect performance, just as synchronized code blocks do above.
So to sum up, using the set() method of a ThreadLocal can store a private variable for the lifetime of a thread and get it when needed. The content of this variable is independent in different threads, which avoids the trouble of passing parameters at multiple levels without losing performance.
public class ThreadLocalDemo2 {
public static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
User user = new User("Flower govemment");
ThreadLocalInfo.userThreadLocal.set(user);
//1. Call the method to get the address
newAddressService().getAddress(); }}class AddressService{
public void getAddress(a){
User user = ThreadLocalInfo.userThreadLocal.get();
System.out.println("According to user information"+user.getUserName()+"Get user address");
//2. Call the coupon method
newTicketService().getTicket(); }}class TicketService{
public void getTicket(a){
User user = ThreadLocalInfo.userThreadLocal.get();
System.out.println("According to user information"+user.getUserName()+"Get user coupons.");
//3. Call send message
newMessageService().sendMessage(); }}class MessageService{
public void sendMessage(a){
User user = ThreadLocalInfo.userThreadLocal.get();
System.out.println("By user"+user.getUserName()+"Send a message"); }}class User {
String userName;
public User(String userName) {
this.userName = userName;
}
public String getUserName(a) {
returnuserName; }}// Create the ThreadLocal variable
class ThreadLocalInfo {
public static ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
}
Copy the code
The explanation of the code is really sweet, clear and clear, the above code in the three steps of getting the address, getting coupons, sending messages, we did not pass the user object layer by layer, but can get the user object content, even if the subsequent demand or process adjustment, we can easily deal with.
I: elder brother dog elder brother, I know usage and function now, that his realization principle can say once?
Thread: ThreadLocal: ThreadLocalMap: ThreadLocalMap: ThreadLocal: ThreadLocalMap
As you can see from the figure, each Thread has a ThreadLocalMap object, and each ThreadLocalMap contains multiple ThreadLocal objects.
Me: This is a picture you drew yourself… How do I know it’s bullshit?!
It took a few days not to learn smart ah, then let’s have a look at the source code. ThreadLocal has four important methods:
public T get(a) {}
public void set(T value) {}
protected T initialValue(a) {}
Copy the code
Let’s take a look at the initialValue() method we overwrote in the first scenario:
protected T initialValue(a) {
return null;
}
Copy the code
Obviously, initialValue() will return a null value if we don’t override it actively.
Let’s look at the set method:
public void set(T value) {
//1. Get the current thread
Thread t = Thread.currentThread();
//2. Obtain the ThreadLocalMap object
ThreadLocalMap map = getMap(t);
//3. If the map exists, the current ThreadLocal object is stored in the map as a key
//this: is the current ThreadLocal
if(map ! =null)
map.set(this, value);
else
//4. If the map does not exist, create it
createMap(t, value);
}
Copy the code
The second step above has a ThreadLocalMap, and what is this? We can actually find the result in the Thread class, which is an inner class of Thread. A ThreadLocalMap stores data in an array of keys called Entry[] table. An Entry can be likened to a map, where the key is a ThreadLocal; Values are what you want to save, such as SimpleDateFormat, user.
ThreadLocalMap is the gray part of the figure above
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<? >>{
/** The value associated with this ThreadLocal. */Object value; Entry(ThreadLocal<? > k, Object v) {super(k); value = v; }}privateEntry[] table; . Omit}Copy the code
Then look at the contents of getMap. It returns the threadLocals object of the current thread, so the first call to set() getMap returns null and createMap is performed on it without overwriting initialValue() to initialize.
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
// Thread.java
// threadLocals defaults to null
ThreadLocal.ThreadLocalMap threadLocals = null;
Copy the code
The createMap operation is also simple, creating a new ThreadLocalMap object that is assigned to the current thread’s threadLocals variable.
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
Copy the code
After looking at the above source code, the get() method is very easy to understand. In step 3, if the set() method has been called before (the threadLocals object is not initialized), the current ThreadLocal object is used as the key to obtain the value stored by threadLocals.
public T get(a) {
//1. Get the current thread
Thread t = Thread.currentThread();
//2. Obtain the threadLocalMap of the current thread
ThreadLocalMap map = getMap(t);
//3. If ThreadLocal is already initialized
if(map ! =null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if(e ! =null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
returnresult; }}//4. if ThreadLocal is not initialized, initialValue() is called to initialize it
return setInitialValue();
}
Copy the code
But what does that last statement do? We’re actually using lazy loading here. When we override initialValue(), it doesn’t initialize immediately, it waits until the first query, and then it initializes by executing setInitialValue().
private T setInitialValue(a) {
// Override the initialValue method
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if(map ! =null)
map.set(this, value);
else
createMap(t, value);
return value;
}
Copy the code
Thread, ThreadLocal, ThreadLocalMap, ThreadLocalMap, ThreadLocal, ThreadLocalMap
I: although very wordy, but really very detailed ah!
Here want to say, every time I said so much, I am also hand acid, mouth also tired, but very worry if ignored some details, a lot of friends may not understand, so more demolished friend can jump see, ignore some of the details, if it is a new friend, that which is easier, more easy to understand.
I: Dog son heart, spend Gie here to give you a thumbs up. So with all the benefits of ThreadLocal, what are the drawbacks?
As always, there are two sides to ThreadLocal, and surely there are some issues to be concerned about, namely memory leaks
Object value; Entry(ThreadLocal<? > k, Object v) {super(k);
value = v;
}
Copy the code
As shown in the above code, value = v indicates that the value we set to ThreadLocal and ThreadLocalMap are strong references. Because a ThreadLocalMap has the same lifetime as a Thread, if the Thread is not destroyed, the ThreadLocal will live forever, and if the corresponding key is not manually deleted, memory leaks will occur.
In the case of a thread pool, for example, using a ThreadLocal can cause a memory leak because threads are always alive. Remove () manually if you want to avoid memory leaks!
I: the dog you this practice two days of effect, I was shocked, give small partners summary.
ThreadLocal sums up the following:
- This object can be easily acquired during the lifetime of the thread;
-
Function:
- Each thread can have its own separate object, isolated from other threads;
- This object can be easily acquired during the lifetime of the thread;
-
Advantages:
- No locking is required to achieve thread safety
- Use memory efficiently and save overhead
- No need to pass parameters layer by layer to achieve code decoupling
-
Scene selection:
- In the case of utility classes, when all threads share an instance, the ThreadLocal variable is initialized when it is created;
- The set() method is flexible when different threads have different objects.
-
Summary of principles:
- ThreadLocalMap is an inner class of threads, and each Thread maintains a reference to ThreadLocalMap
- InitialValue () and set() set key-value pairs essentially the same, essentially calling map.set(this,value)
- The ThreadLocal itself does not store values; it simply obtains values from the ThreadLocalMap as a key.
conclusion
The purpose of using ThreadLocal is not to deal with concurrency or shared variables, but to be able to have their own variables in the current thread, enabling data isolation in the thread.
Next up
Today we are using thread pools. Some people may not know much about thread pools. In the next chapter, we will introduce the use of thread pools. I hope you will continue to pay attention to the dream of Dafang.
Pay attention to avoid getting lost
That’s all for this episode. If you have any mistakes, please leave a comment. Thank you very much. I’m GieGie Flower, feel free to leave a comment if you have any questions, and we’ll see you next time at 🦮.
The article continues to be updated, can be wechat search Java development zero to one first time to read, and can obtain interview materials learning videos, interested partners welcome attention, study together, ha 🐮🥃.
Original is not easy, how can you bear white whore. if you think this article is a little useful to you, thank you for the old iron for this article point a like, comment or forward, because this will be my power to output more quality articles, thank you!
Reference blog:
www.cnblogs.com/dolphin0520…
www.cnblogs.com/xzwblog/p/7…