Point like again see, form a habit, wechat search a search [Aobing] pay attention to the Internet programmer.
This article has been included in GitHub github.com/JavaFamily, there are a line of large factory interview complete test sites, information and my series of articles.
opening
Zhang SAN was in a hot mood recently, so he decided to go out for an interview and chat with the interviewer. As soon as he delivered his resume, someone invited him for an interview.
Oh, my god. How can I get an interview right after I send it out? Aye… True vexed ah, elder brother has not been in the river’s lake for so long, river’s lake still has elder brother’s legend, I still so hot? Too much trouble, handsome not guilty.
Secretly secretly pleased zhang SAN came to the office of some east spot interview, I throw, this interviewer? Really, this scratched Mac, this volume, is the legendary architect?
Zhang SAN’s state of mind collapsed all of a sudden, come out of the first interview to meet a top level interviewer, this who can live.
Hi, I’m your interviewer Tony, judging from my hairstyle, you can guess my identity. I don’t have to say anything. Shall we just get started? I’m not familiar with ThreadLocal, I haven’t written code for a long time, so you can help me remember.
I lost? What the hell is that? ThreadLocal = ThreadLocal = ThreadLocal = ThreadLocal = ThreadLocal = ThreadLocal = ThreadLocal = ThreadLocal Mentality collapse ah, say you TM oneself forget not to know to go down to read a book yao, come to me here to find the answer is what ghost ah…
Reluctantly, John cranked his tiny brain to recall the details of ThreadLocal…
To be honest, I didn’t use ThreadLocal very much in the actual development process. When I wrote this article, I deliberately opened dozens of projects on my computer and searched for ThreadLocal globally. I found that it was rarely used in projects except for system source code, but there were some.
The function of ThreadLocal is mainly for data isolation. The data filled by ThreadLocal only belongs to the current thread. The data of variables is relatively isolated from other threads.
Can you tell me how it works and in what context?
This, I said I rarely use, but also asked me, sad ah, oh, oh, have remembered, transaction isolation level.
Hello, interviewer. In fact, the source code for implementing transaction isolation level in Spring came to my mind at the first time, which I found by accident when I was crying in the library after being dumped by my girlfriend in college.
Spring uses Threadlocal to ensure that database operations in a single thread use the same database connection. At the same time, in this way, the business layer does not need to sense and manage connection objects when using transactions. Switching between multiple transaction configurations can be cleverly managed through propagation level. Suspend and resume.
Spring framework is used inside the ThreadLocal to achieve this isolation, mainly in TransactionSynchronizationManager this class, the code is as follows:
private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<>("Current transaction name");
...
Copy the code
Spring transactions are implemented primarily by ThreadLocal and AOP, and you should know that each thread’s own links are maintained by ThreadLocal. I will continue to discuss these details in the Spring chapter.
In addition to the scenarios using ThreadLocal in the source code, do you have any scenarios using it yourself? How would you normally use it?
Here we go, here we go, here we go, here’s my chance to play B.
Some some interviewers, this I can!!
When we went online, we found that some users’ dates were wrong. We found that it was the SimpleDataFormat cooker. At that time, we used SimpleDataFormat’s parse() method and there was a Calendar object inside. The parse() method calling SimpleDataFormat calls calendar.clear () and then calendar.add (). If one thread calls Add () and then another thread calls clear(), The parse() method is at the wrong time.
To solve this problem, it is easy to let each thread new its own SimpleDataFormat, but 1000 threads new1000 SimpleDataFormat?
So we wrapped SimpleDataFormat with a thread pool and ThreadLocal, and then called initialValue to give each thread a copy of SimpleDataFormat, which solved the thread-safety problem and improved performance.
That……
And also, I have, you don’t try so hard to ask the next, let me add some more points, delay the interview time.
I have a thread in the project that often encounters the object that needs to be passed across several method calls, namely the Context, which is a state, usually the user identity, task information, etc., and there will be the problem of over-passing parameters.
Adding a context parameter to each method can be cumbersome using a chain of responsibility pattern, and sometimes object parameters can’t be passed in if the calling chain has a third-party library that can’t modify the source code, so I used ThreadLocal to make a change. You just need to set the parameters in ThreadLocal before the call and get them elsewhere.
before
void work(User user) {
getInfo(user);
checkInfo(user);
setSomeThing(user);
log(user);
}
then
void work(User user) {
try{
threadLocalUser.set(user);
// Their internal User u = threadLocaluser.get (); It's good
getInfo();
checkInfo();
setSomeThing();
log();
} finally {
threadLocalUser.remove();
}
}
Copy the code
I looked at a lot of scenarios where cookie, session and other data isolation is implemented with ThreadLocal.
In Android, the Looper class takes advantage of ThreadLocal to ensure that only one Looper object exists per thread.
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
if(sThreadLocal.get() ! =null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
Copy the code
Interviewer: I don’t know, how does this guy know so many scenes? Also pulled out Android, come on Sir, I’m going to test his principle.
Yeah, that’s a good answer. Can you tell me about the underlying implementation?
Good interviewer, let me talk about his use:
ThreadLocal<String> localName = new ThreadLocal();
localName.set("Zhang");
String name = localName.get();
localName.remove();
Copy the code
It’s really easy. The thread comes in and initializes a generic ThreadLocal object, and then whenever the thread gets before remove, it gets the set value.
It can isolate data between threads, so there is no way for another thread to get the value of another thread using get (), but there is a way to do it, as I’ll explain later.
Let’s look at the source of his set:
public void set(T value) {
Thread t = Thread.currentThread();// Get the current thread
ThreadLocalMap map = getMap(t);// Get the ThreadLocalMap object
if(map ! =null) // Check whether the object is empty
map.set(this, value); // not null set
else
createMap(t, value); // Create a map object for null
}
Copy the code
As you can see, the source code for the set is very simple. The main thing to look at is the ThreadLocalMap, which is obtained from a variable called threadLocals for the current Thread.
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
Copy the code
public class Thread implements Runnable {
...
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/ *
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
* /
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
...
Copy the code
Each Thread maintains its own threadLocals variable, so when each Thread creates a ThreadLocal, The data is actually stored in the Thread locals variable, which is not available to others.
What does the ThreadLocalMap underlying structure look like?
The interviewer asked this question very well, the heart secretly scold, let me rest for a while can’t?
Zhang SAN answered with a smile, since there is a Map that his data structure is actually very similar to HashMap, but you can see the source code, it does not implement Map interface, and his Entry is the inheritance of WeakReference (WeakReference), also did not see next in HashMap, so there is no linked list.
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;
}
}
...
}
Copy the code
The structure looks something like this:
Hold on. I have two questions. Can you answer them?
Yes, the interviewer.
Why do we need an array? How do you resolve Hash conflicts without linked lists?
We use arrays because we can have multiple TreadLocal for different types of objects on a single thread during development, but they will all be placed in the ThreadLocalMap of your current thread, so you definitely want arrays.
For Hash collisions, let’s take a look at the source code:
private void set(ThreadLocal
key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e ! =null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<? > k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if(! cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
Copy the code
ThreadLocalHashCode will be assigned to each ThreadLocal object when it is stored. During the insertion process, the hash value of the ThreadLocal object will be positioned at position I in the table. Int I = key.threadLocalHashCode & (len-1).
If the current position is empty, it initializes an Entry object and places it at position I.
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
Copy the code
If position I is not empty, and if the key of the Entry object happens to be the key to be set, then the value in the Entry is refreshed;
if (k == key) {
e.value = value;
return;
}
Copy the code
If position I is not empty and key is not equal to entry, the next empty position is found until it is empty.
In this case, during the get, the hash value of the ThreadLocal object will be used to locate the position in the table, and then determine whether the key in the Entry object of this position is the same as the key of the GET. If not, the next position will be determined. If the set and GET conflict seriously, The efficiency is still very low.
Here is the source code for get, does it feel very easy to understand:
private Entry getEntry(ThreadLocal
key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if(e ! =null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
private Entry getEntryAfterMiss(ThreadLocal<? > key,int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
If (e! = 1) {if (e! = 1) {if (e! = 1) { = NULL && LLDB () == key).
while(e ! =null) {
ThreadLocal<? > k = e.get();
// If it is equal, return it directly. If it is not equal, continue to search for it.
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
Copy the code
Can you tell me where objects are stored?
In Java, stack memory is owned by a single thread. Each thread has one stack memory, and the variables it stores are visible only to the thread it belongs to. That is, stack memory can be understood as the thread’s private memory, while objects in heap memory are visible to all threads, and objects in heap memory can be accessed by all threads.
ThreadLocal instances and their values are stored on the stack.
It’s not, because ThreadLocal instances are actually held by the class they create (and at the top by the thread), and ThreadLocal values are actually held by the thread instance. They’re all on the heap, but with a trick of making them visible to the thread.
What if I want to share ThreadLocal data for a thread?
Use InheritableThreadLocal to create an instance of InheritableThreadLocal that can be accessed by multiple threads. We then get the value set by this InheritableThreadLocal instance in the child thread.
private void test(a) {
final ThreadLocal threadLocal = new InheritableThreadLocal();
threadLocal.set("As handsome as a dog.");
Thread t = new Thread() {
@Override
public void run(a) {
super.run();
Log.i( "Is Sam handsome?" + threadLocal.get());
}
};
t.start();
}
Copy the code
In the child thread, I can normally output that line of log, which is also the problem I mentioned in the previous interview video about data transfer between parent and child threads.
How is it delivered?
The logic for passing is simple. When I mention threadLocals at the beginning of the Thread code, you’ll see that I’ve deliberately added another variable:
Init init init init init init init init init
public class Thread implements Runnable {
...
if(inheritThreadLocals && parent.inheritableThreadLocals ! =null)
this.inheritableThreadLocals=ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
...
}
Copy the code
If the thread’s inheritThreadLocals variable is not empty, as in our example above, and the parent thread’s inheritThreadLocals also exists, So I’m going to give the inheritThreadLocals of the parent thread to the inheritThreadLocals of the current thread.
Isn’t that interesting?
You know a lot about ThreadLocal, so you’re a deep user of ThreadLocal. Do you see any problems with ThreadLocal?
Are you talking about memory leaks?
Damn it. How does this guy know what I’m gonna ask him? Yeah, yeah, you say it.
That’s going to happen, and I’ll tell you why. Remember my code up here?
When ThreadLocal is saved, it will treat itself as a Key in ThreadLocalMap. Normally, both Key and value should be strongly referenced by the outside world, but now Key is designed as WeakReference.
Let me introduce you to weak references:
Objects with only weak references have a shorter life cycle, and when the garbage collector thread scans the memory area under its control, once it finds an object with only weak references, it reclaims its memory regardless of whether the current memory space is sufficient or not.
However, because the garbage collector is a low-priority thread, objects that have only weak references are not necessarily found quickly.
This leads to the problem that ThreadLocal without strong external references will be reclaimed during GC. If the thread that created the ThreadLocal keeps running, then the value in the Entry object may not be reclaimed and will leak memory.
For example, if a thread in a thread pool is reused, the thread is still alive after the previous thread instance is processed, so the value set by ThreadLocal is held, causing a memory leak.
ThreadLocalMap is supposed to be emptied after a thread is used, but the thread is being reused.
So what’s the solution?
Use remove at the end of the code, just remember to empty the value with remove at the end of the code.
ThreadLocal<String> localName = new ThreadLocal();
try {
localName.set("Zhang");
...
} finally {
localName.remove();
}
Copy the code
Remove source code is very simple, find the corresponding value of all empty, so that when the garbage collector to collect, it will automatically reclaim them.
So why are ThreadLocalMap keys designed to be weak references?
If the key is not set as a weak reference, the same memory leak scenario occurs as the value in the entry.
I think we can make up for the lack of ThreadLocal by taking a look at Netty’s fastThreadLocal.
Well, you not only answered all my questions, you even told me that you passed ThreadLocal, but the interview of JUC has just started. I hope you can make progress and get a good offer in the future.
What the hell, all of a sudden so sentimental, isn’t it very difficult for me? Is it to train me? Difficult for the master so for my sake, I have been in the heart of the dark scold him, do not say to go back to learn.
conclusion
ThreadLocal is pretty simple to use. There are only a few methods in it, and even with the annotated source code, it took me a little more than 10 minutes to go through them, but as I dug into the logic behind each method, I had to marvel at the genius of Josh Bloch and Doug Lea.
In fact, the processing of detail design is often the difference between us and the great god. I think many unreasonable points are found only after the in-depth understanding of Google and myself, which is reasonable. I really refuse to accept it.
ThreadLocal is one of the most popular classes in multithreading. It is not used as often as other methods and classes.
I’m Aobing, and the more you know, the more you don’t know, and I’ll see you next time!
Talented people’s [three lian] is the biggest power of aobing’s creation, if there are any mistakes and suggestions in this blog, welcome talented people to leave a message!
This article will continue to be updated, you can search “Aobing” on wechat for the first time to read, after attention to reply [information] I prepared the interview materials and resume template, GitHub github.com/JavaFamily has included this article, there is a complete test site for the interview of big factory, welcome Star.