summarize
ThreadLocal variables. When a variable is maintained using ThreadLocal, ThreadLocal provides an independent copy of the variable for each thread that uses it, so each thread can change its own copy independently without affecting other threads.
Each thread has a ThreadLocalMap (a ThreadLocal internal class), and the elements of the MapThe key is ThreadLocal. and the value corresponds to a copy of the thread's variable.
ThreadLocal principle
How to implement thread isolation
The code specific to assigning copies of variables to threads is as follows.
public T get() {
Thread t = ();
ThreadLocalMap threadLocals = getMap(t);
if (threadLocals != null) {
e = (this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T);
return result;
}
}
return setInitialValue();
}
-
First get the current thread object t, then get the ThreadLocals property of the ThreadLocalMap from thread t.
-
If the threadLocals of the current thread have been initialized (i.e., not null) and there is a value with the current ThreadLocal object as the key, then return the object that the current thread is trying to retrieve (in this case, Connection).
-
If the threadLocals of the current thread have been initialized (i.e., not null) but there is no object keyed to the current ThreadLocal object, then recreate a Connection object and add it to the threadLocals Map of the current thread and return it.
-
If the threadLocals property of the current thread has not been initialized, create a new ThreadLocalMap object, and create a Connection object and add it to the ThreadLocalMap object and return it.
It's easy to understand how to return if it exists, but what about the code for how to initialize it?
private T setInitialValue() {
T value = initialValue();
Thread t = ();
ThreadLocalMap map = getMap(t);
if (map != null)
(this, value);
else
createMap(t, value);
return value;
}
-
First call the overloaded initialValue method written above.
-
Continue to see if the threadLocals of the current thread are empty, if the ThreadLocalMap has been initialized, then add the resulting object directly to the ThreadLocalMap, if not initialized, then create and add an object to it; and
At the same time, ThreadLocal also provides methods to manipulate threadLocals directly in Thread objects.
public void set(T value) {
Thread t = ();
ThreadLocalMap map = getMap(t);
if (map != null)
(this, value);
else
createMap(t, value);
}
This would also allow you to not implement the initialValue:
public Connection getConnection() {
Connection connection = ();
if (connection == null) {
try {
connection = ("", "", "");
(connection);
} catch (SQLException e) {
();
}
}
return connection;
}
After looking at the code, it is clear why ThreadLocal can achieve multi-threaded isolation of variables; in fact, it is the use of the Map data structure to the current thread cache, when you want to use it from the thread of the threadLocals object to get on it, the key is the current thread.
Of course, in the current thread to get the current thread in the Map object and operation of the current thread there is certainly no thread concurrency problems, of course, can be done to isolate the variables between threads;
What is a ThreadLocalMap object
Essentially, it is a Map, but this ThreadLocalMap is a little different from the usual Map
-
It does not implement the Map interface; the
-
It does not have public methods, at most, there is a default constructor, because the ThreadLocalMap method is called only in the ThreadLocal class, belongs to the static internal class
-
The Entry implementation of ThreadLocalMap inherits WeakReference<ThreadLocal<? >>
-
This method just uses an Entry array to store Key, Value; Entry is not in the form of a chained list, but just one Entry per bucket.
To understand the implementation of a ThreadLocalMap, let's start with the entry point, which is adding a value to the Map: the
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = ;
int i = & (len-1);
//Here's what's usedHashLinear detection of conflicting open addressing methods
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = ();
if (k == key) {
= 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();
}
Let's start with a brief analysis to interpret the meaning of the code at the surface level.
-
Look at the current threadLocal index position in the array for example: i = 2, see i = 2 position above the element (Entry) Key is equal to the threadLocal Key, if equal to it is very good, directly above the position of the Entry's Value will be replaced with the latest on the line.
-
If the Key of the Entry above the current position is empty, the ThreadLocal object has been recycled, then call replaceStaleEntry.
-
If after cleaning up the useless entries (ThreadLocal is reclaimed entries), and the data size in the array > threshold when the current Table to re-hash So, the HashMap is to deal with the mechanism of conflict detection is to shift backward, clear the expired entries and finally find the appropriate location;
After the Set method, it's time for the Get method.
private Entry getEntry(ThreadLocal<?> key) {
int i = & ( - 1);
Entry e = table[i];
if (e != null && () == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
First find the index location of ThreadLocal, if the entry at the index location is not null and the key is the same object as threadLocal, then return directly; otherwise, go to the next index location and continue to search.
Entry object
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);//The parent class isWeakReference,That is, the equivalent ofnewhas a weak reference to the(k)
//which is equivalent to maphit the nail on the headkeyis weakly referenced
value = v;
}
}
The weak reference to ThreadLocal in key is to prevent the ThreadLocal object from ever being reclaimed. Because, if key is a strong reference, when ThreadLocal doesn't want to use it, then make tl = null, but at this time, there is still a strong reference in key pointing to ThreadLocal, and therefore it can never be reclaimed (unless ThreadLocalMap is not in use), so there is memory leakage; however, if the key is using a weak reference, it will be reclaimed whenever the GC, it will be reclaimed
However, there is still a memory leak, ThreadLocal is recycled, which leads to key=null, at this time the map can not access the value, the value can not be accessed is useless, that is to say, the k-v pair is useless, then the value should be recycled, but in fact, the value may not have been recycled, and therefore there is still a memory leak. leak
Memory Leak (Memory Leak) refers to the program has been dynamically allocated heap memory due to some reason the program is not released or can not be released, resulting in a waste of system memory, resulting in a slowdown of the program or even system crash and other serious consequences.
Weak references: If there are no more strong references to the object and only weak references are left, the object will be reclaimed during GC. The reason for this is that weak references are reclaimed by GC regardless of whether there is enough memory. So, as long as tl=null, the ThreadLocal object pointed to by the key will be reclaimed during GC.
Cause of ThreadLocal memory leak?
Each thread has an internal attribute of ThreadLocalMap. The key of the map is ThreaLocal, defined as a weak reference, and the value is a strong reference. The key is automatically recycled during garbage collection, and the recycling of the value depends on the life cycle of the Thread object.
Generally, we will save resources by multiplexing threads through thread pooling, but if we use thread pooling to operate ThreadLocal objects will cause memory leaks, because for threads that will not be destroyed in the thread pool, there will always be strong references to <ThreadLocal, LocalVariable> inside the ThreadLocal, LocalVariable, and the ThreadLocalMap will not be released, while ThreadLocalMap is a weak reference to Key, but the strong reference will not be released, and the weak reference will always have a value, and the LocalVariable created will also have a value. ThreadLocal will not be released, and ThreadLocalMap for the Key is a weak reference, but the strong reference will not be released, the weak reference of course will always have a value, at the same time the creation of the LocalVariable object will not be released, resulting in memory leaks; if the LocalVariable object is not a large object, the leak is not serious. If the LocalVariable object is not a large object, in fact, the leak is not serious, the leak of memory = the number of core threads * LocalVariable object size.
Therefore, in order to avoid memory leaks, ThreadLocal provides a method to clear the object in the thread, i.e. remove, in fact, the internal implementation is to call ThreadLocalMap's remove method.
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = ;
int i = & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (() == key) {
();
expungeStaleEntry(i);
return;
}
}
}
application scenario
Each thread maintains a "sequence number".
public class SerialNum {
// The next serial number to be assigned
private static int nextSerialNum = 0;
private static ThreadLocal serialNum = new ThreadLocal() {
protected synchronized Object initialValue() {
return new Integer(nextSerialNum++);
}
};
public static int get() {
return ((Integer) (())).intValue();
}
}
Session management
Request processing in Web applications: In Web applications, a request is usually processed by multiple threads, and each thread needs to access its own data, using ThreadLocal ensures that the data is independent in each thread.
Another example of a classic:
private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() throws InfrastructureException {
Session s = (Session) ();
try {
if (s == null) {
s = getSessionFactory().openSession();
(s);
}
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}
Creating a ThreadLocal inside a thread
Thread objects in a thread pool share data: Thread objects in a thread pool can be shared by multiple tasks. If you need to save task-related data in a thread object, use ThreadLocal to ensure thread safety.
Of course, when using thread pools, ThreadLocal may lead to data residuals when threads are reused, thus affecting the correctness of the program. Therefore, when using thread pools, make sure to clean up the value of ThreadLocal before and after task execution to avoid data residuals when threads are reused.
The basic steps to create a ThreadLocal inside a thread class are as follows:
-
In a multi-threaded class (such as the ThreadDemo class), create a ThreadLocal object threadXxx to hold objects xxx that need to be handled in isolation between threads.
-
In the ThreadDemo class, create a method getXxx() to get the data to be accessed in isolation, determine in the method, if the ThreadLocal object is null, you should new() an object of the type of isolation access, and force the conversion to the type to be applied.
-
In the run() method of the ThreadDemo class, the data to be operated is obtained by calling the getXxx() method, which ensures that each thread corresponds to a data object, and it is this object that is being operated at any given moment.
public class ThreadLocalTest implements Runnable{
ThreadLocal<Student> StudentThreadLocal = new ThreadLocal<Student>();
@Override
public void run() {
String currentThreadName = ().getName();
(currentThreadName + " is running...");
Random random = new Random();
int age = (100);
(currentThreadName + " is set age: " + age);
Student Student = getStudentt(); //Through this method,for each thread independently of thenewanStudenttboyfriend,Each thread'sStudenttboyfriend都可以设置不同的值
(age);
(currentThreadName + " is first get age: " + ());
try {
(500);
} catch (InterruptedException e) {
();
}
( currentThreadName + " is second get age: " + ());
}
private Student getStudentt() {
Student Student = ();
if (null == Student) {
Student = new Student();
(Student);
}
return Student;
}
public static void main(String[] args) {
ThreadLocalTest t = new ThreadLocalTest();
Thread t1 = new Thread(t,"Thread A");
Thread t2 = new Thread(t,"Thread B");
();
();
}
}
class Student{
int age;
public int getAge() {
return age;
}
public void setAge(int age) {
= age;
}
}
ThreadLocal recommended in the java developer's manual.
Take a look at the recommended usage of ThreadLocal in the Alibaba java development handbook.
import ;
import ;
public class DateUtils {
public static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
}
Then where you want to use the DateFormat object, call it like this:
().format(new Date());
Interview questions column
Java interview questions columnIt's online, so feel free to visit.
- If you don't know how to write a resume, resume projects don't know how to package them;
- If there's something on your resume that you're not sure if you should put on it or not;
- If there are some comprehensive questions you don't know how to answer;
Then feel free to private message me and I will help you in any way I can.