• 周六. 10 月 5th, 2024

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

Java Concurrent Programming: application of ThreadLocal and analysis of its implementation principle

King Wang

1 月 3, 2022

Preface

In the previous post , We learned about the use of locks , The mechanism of locking is to ensure that only one thread can access the resources of the critical section at a time , That is, by controlling resources to ensure thread safety , This is certainly an effective tool , However, the running efficiency of the program is greatly reduced . that , There’s no better way ? The answer is yes , Since locking is a way of strictly controlling resources to ensure thread safety , So we can do the opposite , Add more resources , Ensure that each thread gets the desired object , Its current , They don’t influence each other , In order to achieve the purpose of thread safety , and ThreadLocal That’s the idea .

ThreadLocal example

ThreadLocal If translated into Chinese, you might say yes : Thread-local variable , That is, only the current thread can access it . It is designed to provide a copy of the value of the variable for each thread that USES it , Each thread changes its own copy and does not conflict with the copy of another thread , thus , From the point of view of threads , It’s as if every thread has that variable .

Here is a simple example :

public class ThreadLocalDemo {
static ThreadLocal<Integer> local = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 0;
}
};
public static class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0;i<3;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int value = local.get();
System.out.println(Thread.currentThread().getName() + ":" + value);
local.set(value + 1);
}
}
}
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
t1.start();
t2.start();
}
}

The above code is not hard to understand , The first is to define a name local Of ThreadLocal Variable , And the initial value of the variable is 0, Then an implementation is defined Runnable The inner class of the interface , In its run Method for local The value of 1 The operation of , And finally main Method to start two threads to run the inner class instance .

That’s the general logic of the code , function main After the function , The output of the program is as follows :

Thread-0:0
Thread-1:0
Thread-1:1
Thread-0:1
Thread-1:2
Thread-0:2

It can be seen from the results , Even though both threads share one Runnable example , But what’s shown in both threads ThreadLocal The data values do not affect each other , So in this case local Variables hold data that is thread-safe , Can only be accessed by the current thread .

ThreadLocal Realization principle

that ThreadLocal How do you guarantee internally that an object is thread-private ? without doubt , The answer needs to be found in the source code . Review the previous code , You can see that it’s called ThreadLocal Two approaches set and get, So let’s start with these two methods .

First look at set() Source code :

public void set(T value) {
Thread t = Thread.currentThread();
// Get threaded ThreadLocalMap, return map
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
//map It's empty , establish
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

set The logic of the code is relatively simple , This is mainly one that sets the value to the current thread ThreadLocalMap In the object , and ThreadLocalMap You can think of it as one Map, It is defined in Thread An internal member of a class , Initialization is zero null,

ThreadLocal.ThreadLocalMap threadLocals = null;

however , With the common Map Implementation class , Such as HashMap And so on ,ThreadLocalMap Medium Entry It is inherited from WeakReference Class , To keep the “ key ” Weak references and pairs “ value ” A strong reference to , This is the source of the class :

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;
}
}
// Omit the rest of the source code
....................
}

You can see it in the source code ,Entry Arguments in the constructor k Namely ThreadLocal example , call super(k) Show that you are right k Is a weak reference , The reason for using weak references is this , When there is no strong reference to point to ThreadLocal When an instance , It can be recycled , To avoid memory leaks , So why do you need to prevent memory leaks ? Here’s why .

And then say set The logic of the method , When calling set When the method is used , It’s actually writing data threadLocals This Map In the object , This Map Of key by ThreadLocal The current object ,value That’s what we put in there . and threadLocals Itself can hold more than one ThreadLocal object , Equivalent to one ThreadLocal aggregate .

Then look at get() Source code :

public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// Set the initial value to ThreadLocal And in return
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}

get The logic of the method is also simple , That is to get the current thread directly ThreadLocalMap object , Returns its value if the object is not empty value value , Otherwise, set the initial value to ThreadLocal And in return .

See this , We can sort of see why ThreadLocal Can implement the principle of thread private , Each thread maintains one ThreadLocal The container of , The container is ThreadLocalMap, You can save multiple ThreadLocal object . And call ThreadLocal Of set or get Method is for the current thread ThreadLocal Variable operating , It is separate from other threads , So you can keep threads private , There is no thread safety problem .

However , This scheme ensures that threads are private , But it takes up a lot of memory , Because each thread maintains one Map, When accessing a ThreadLocal After the variable , The thread will be on its own Map Maintained within the ThreadLocal Mapping of variables to concrete implementations , If these mappings exist all the time , That means ThreadLocal There is a reference case , So the system GC You can’t reclaim these variables , Memory leaks can occur .

In this case , Above mentioned ThreadLocalMap in Entry The weak reference of .

TheadLocal The difference with the synchronization mechanism

Last , To sum up ThreadLocal And the synchronization mechanism .

Implementation mechanism :

The synchronization mechanism was adopted “ Time for space ” The way , The control resource ensures that only one thread can be accessed at a time .

ThreadLocal Adopted “ Space for time ” The way , Each thread is provided with a copy of the variable , In order to achieve simultaneous access without affecting each other , But because each thread maintains a copy , Memory footprint increases .

Data sharing :

Synchronization is a way to control access to common resources to ensure thread safety , But the resource is still in a Shared state , Can be used for communication between threads ;

ThreadLocal Each thread has its own resources ( Variable ) copy , It doesn’t affect each other , There is no such thing as sharing .

发表回复