当前位置:网站首页>[JUC series] ThreadLocal of synchronization tool class
[JUC series] ThreadLocal of synchronization tool class
2022-06-29 12:22:00 【Gu Dong】
ThreadLocal analysis
List of articles
ThreadLocal Definition
This provides Thread-local variable . This variable is different from the general variable , Because every access to this variable ( adopt ThreadLocal Of get or set Method ) All threads have their own 、 Independently initialized variable copies .ThreadLocal An instance is usually a private static field in a class that wants to associate a state with a thread ( for example , user ID Or business ID).
As long as the thread is active and ThreadLocal Instance can access , Each thread holds an implicit reference to a copy of its thread local variable ; After the thread disappears , All copies of its thread local instances will be garbage collected ( Unless there are other references to these copies ).
Example
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadLocalDemo {
private static final ThreadLocal<String> THREAD_LOCAL_1 = ThreadLocal.withInitial(() -> "0s");
private static final ThreadLocal<String> THREAD_LOCAL_2 = ThreadLocal.withInitial(() -> "00s");
private static final CountDownLatch COUNT_DOWN_LATCH = new CountDownLatch(10);
private static final ThreadPoolExecutor POOL_EXECUTOR = initThreadPool(5, 10, 1000);
/** * The worker thread */
public static class WorkerThreadFactory implements ThreadFactory {
private final String namePrefix;
private final AtomicInteger nextId = new AtomicInteger(1);
WorkerThreadFactory(String whatFeatureOfGroup) {
this.namePrefix = "From WorkerThreadFactory's " + whatFeatureOfGroup + "-Worker-";
}
@Override
public Thread newThread(Runnable task) {
String name = namePrefix + nextId.getAndIncrement();
return new Thread(null, task, name, 0);
}
}
/** * Initializes the thread pool */
public static ThreadPoolExecutor initThreadPool(int corePoolSize, int maxPoolSize, long keepAliveTime) {
return new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000),
new WorkerThreadFactory("ThreadLocalDemo"),
new ThreadPoolExecutor.AbortPolicy());
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
int finalI = i + 1;
POOL_EXECUTOR.execute(() -> {
System.out.println("[" + new SimpleDateFormat("HH:mm:ss.SSS").format(new Date())
+ "]--[" + Thread.currentThread().getName() + "] THREAD_LOCAL_1, threadId:" + Thread.currentThread().getId() + ", " + THREAD_LOCAL_1.get());
System.out.println("[" + new SimpleDateFormat("HH:mm:ss.SSS").format(new Date())
+ "]--[" + Thread.currentThread().getName() + "] THREAD_LOCAL_2, threadId:" + Thread.currentThread().getId() + ", " + THREAD_LOCAL_2.get());
THREAD_LOCAL_1.set("THREAD_LOCAL_1 is " + finalI);
THREAD_LOCAL_2.set("THREAD_LOCAL_2 is " + finalI);
System.out.println("[" + new SimpleDateFormat("HH:mm:ss.SSS").format(new Date())
+ "]--[" + Thread.currentThread().getName() + "] THREAD_LOCAL_1, threadId:" + Thread.currentThread().getId() + ", " + THREAD_LOCAL_1.get());
System.out.println("[" + new SimpleDateFormat("HH:mm:ss.SSS").format(new Date())
+ "]--[" + Thread.currentThread().getName() + "] THREAD_LOCAL_2, threadId:" + Thread.currentThread().getId() + ", " + THREAD_LOCAL_2.get());
THREAD_LOCAL_1.remove();
THREAD_LOCAL_2.remove();
COUNT_DOWN_LATCH.countDown();
});
}
if (COUNT_DOWN_LATCH.await(2, TimeUnit.MINUTES)) {
System.out.println("[" + new SimpleDateFormat("HH:mm:ss.SSS").format(new Date()) + "]--[" + Thread.currentThread().getName() + "] is over.");
}
POOL_EXECUTOR.shutdown();
}
}
Execution results
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-3] THREAD_LOCAL_1, threadId:13, 0s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-4] THREAD_LOCAL_1, threadId:14, 0s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-1] THREAD_LOCAL_1, threadId:11, 0s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-3] THREAD_LOCAL_2, threadId:13, 00s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-1] THREAD_LOCAL_2, threadId:11, 00s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-3] THREAD_LOCAL_1, threadId:13, THREAD_LOCAL_1 is 3
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-2] THREAD_LOCAL_1, threadId:12, 0s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-3] THREAD_LOCAL_2, threadId:13, THREAD_LOCAL_2 is 3
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-5] THREAD_LOCAL_1, threadId:15, 0s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-3] THREAD_LOCAL_1, threadId:13, 0s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-5] THREAD_LOCAL_2, threadId:15, 00s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-3] THREAD_LOCAL_2, threadId:13, 00s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-2] THREAD_LOCAL_2, threadId:12, 00s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-3] THREAD_LOCAL_1, threadId:13, THREAD_LOCAL_1 is 6
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-1] THREAD_LOCAL_1, threadId:11, THREAD_LOCAL_1 is 1
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-4] THREAD_LOCAL_2, threadId:14, 00s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-1] THREAD_LOCAL_2, threadId:11, THREAD_LOCAL_2 is 1
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-3] THREAD_LOCAL_2, threadId:13, THREAD_LOCAL_2 is 6
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-4] THREAD_LOCAL_1, threadId:14, THREAD_LOCAL_1 is 4
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-1] THREAD_LOCAL_1, threadId:11, 0s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-1] THREAD_LOCAL_2, threadId:11, 00s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-4] THREAD_LOCAL_2, threadId:14, THREAD_LOCAL_2 is 4
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-1] THREAD_LOCAL_1, threadId:11, THREAD_LOCAL_1 is 7
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-4] THREAD_LOCAL_1, threadId:14, 0s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-1] THREAD_LOCAL_2, threadId:11, THREAD_LOCAL_2 is 7
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-4] THREAD_LOCAL_2, threadId:14, 00s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-1] THREAD_LOCAL_1, threadId:11, 0s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-4] THREAD_LOCAL_1, threadId:14, THREAD_LOCAL_1 is 9
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-1] THREAD_LOCAL_2, threadId:11, 00s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-4] THREAD_LOCAL_2, threadId:14, THREAD_LOCAL_2 is 9
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-1] THREAD_LOCAL_1, threadId:11, THREAD_LOCAL_1 is 10
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-1] THREAD_LOCAL_2, threadId:11, THREAD_LOCAL_2 is 10
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-2] THREAD_LOCAL_1, threadId:12, THREAD_LOCAL_1 is 2
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-2] THREAD_LOCAL_2, threadId:12, THREAD_LOCAL_2 is 2
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-5] THREAD_LOCAL_1, threadId:15, THREAD_LOCAL_1 is 5
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-3] THREAD_LOCAL_1, threadId:13, 0s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-5] THREAD_LOCAL_2, threadId:15, THREAD_LOCAL_2 is 5
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-3] THREAD_LOCAL_2, threadId:13, 00s
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-3] THREAD_LOCAL_1, threadId:13, THREAD_LOCAL_1 is 8
[17:59:01.646]--[From WorkerThreadFactory's ThreadLocalDemo-Worker-3] THREAD_LOCAL_2, threadId:13, THREAD_LOCAL_2 is 8
[17:59:01.646]--[main] is over.
form
Inner class SuppliedThreadLocal
ThreadLocal An extension of , Get its initial value from the specified supplier .
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
private final Supplier<? extends T> supplier;
SuppliedThreadLocal(Supplier<? extends T> supplier) {
this.supplier = Objects.requireNonNull(supplier);
}
@Override
protected T initialValue() {
return supplier.get();
}
}
Inner class ThreadLocalMap
ThreadLocalMap Is a custom hash map , Only for maintaining thread local values . Not in ThreadLocal Class . This class is package private , To allow in class Thread Declare fields in ,ThreadLocal.ThreadLocalMap threadLocals. The hash table Entry Inherited WeakReferences As key, its key The value of is ThreadLocal,value by ThreadLocal Corresponding value . threadLocal.set(5) Will threadLocal and 5 Stored in the thread as a key value pair threadLocals in .
Construction method
This is an empty constructor , If you need to set the initial value , You can call withInitial.
public ThreadLocal() {
}
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
return new SuppliedThreadLocal<>(supplier);
}
The core approach
| Method name | describe |
|---|---|
set(T value) | Sets the current thread copy of this thread local variable to the specified value . Most subclasses do not need to override this method , Rely solely on initialValue Method to set the value of thread local variables . |
T get() | Returns the value in the current thread copy of this thread local variable . If the variable has no value for the current thread , It is initialized to call initialValue The value returned by the . |
void remove() | Delete the current thread value of this thread local variable . If this thread local variable is subsequently read by the current thread , Its value will be determined by calling its initialValue Method reinitializes , Unless its value is set in the middle by the current thread . This may result in multiple calls in the current thread initialValue Method . |
set process analysis
void set(T value)
Set the value for the copy of the current thread in the thread part
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
- Get the current thread first , adopt getMap Method to get the of the corresponding thread threadLocals Variable
- If threadLocals Variable is not empty , Call set Method to set the thread local variable copy of the current thread value
- If threadLocals The variable is empty , Call createMap Method , establish ThreadLocalMap, It also sets the value of the thread local variable copy . In the ThreadLocalMap Object assigned to threadLocals Variable .
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
Thread Class threadLocals
/* Related to this thread ThreadLocal value . This mapping is by ThreadLocal Class maintenance . */
ThreadLocal.ThreadLocalMap threadLocals = null;
set(ThreadLocal<?> key, Object value)
private void set(ThreadLocal<?> key, Object value) {
//
Entry[] tab = table;
int len = tab.length;
// 1. adopt key Of hashcode Calculate the location of the index
int i = key.threadLocalHashCode & (len-1);
// 2. Traverse from the index position , adopt nextIndex Method to find the next index location
for (Entry e = tab[i];
e != null; // What does this line of code mean ?
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
// When key And Entry[] Array k identical , Update value.
if (k == key) {
e.value = value;
return;
}
// If you traverse to a Entry Of k It's empty , It needs to be emptied key by null Of Entry
if (k == null) {
// Keep looking for key The stored subscript of , And clean up key Empty Entry
replaceStaleEntry(key, value, i);
return;
}
}
// If you pass nextIndex Find an empty location ( The representative did not find key same ), Then put the element in this position
tab[i] = new Entry(key, value);
int sz = ++size;
// call cleanSomeSlots Methods to clean up key by null Of Entry, And judge whether expansion is needed , Call... If necessary rehash Methods to expand capacity
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
nextIndex(int i, int len)
/** stay len Find... In length i The subscript after that */
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot)
private void replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot) {
Entry[] tab = table;
int len = tab.length;
Entry e;
// Clear the start position of the element
int slotToExpunge = staleSlot;
// From the position staleSlot Traversal forward , Until I met Entry It's empty
for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len))
if (e.get() == null)
slotToExpunge = i; // use staleSlot Record the top of the array key by null The location of i)
// From the position staleSlot Traverse backward , Until I met Entry It's empty
for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
// If transmitted key And ergodic i Of key identical
if (k == key) {
// to update i Location Entry Of value value
e.value = value;
// In exchange for i Position and staleSlot The element of location (staleSlot The position is in the front , Is the element to be cleared )
tab[i] = tab[staleSlot];
tab[staleSlot] = e;
// If equal , Then represent staleSlot There is no... In the previous subscript key by null Of , What needs to be cleared is that the current subscript is i The elements of
if (slotToExpunge == staleSlot)
slotToExpunge = i;
// from slotToExpunge Position starts to clear key Empty elements
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
return;
}
// Traversing key==null also staleSlot There is no... In the previous subscript key by null Of , Then use staleSlot Record the top of the array key by null The location of i
if (k == null && slotToExpunge == staleSlot)
slotToExpunge = i;
}
// The code goes here to explain , There is no corresponding key, Need to be in staleSlot Create a new location Entry
tab[staleSlot].value = null;
tab[staleSlot] = new Entry(key, value);
// If set up , Note that there are other positions in the array key by null To deal with
if (slotToExpunge != staleSlot)
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
}
cleanSomeSlots(int i, int n)
private boolean cleanSomeSlots(int i, int n) {
boolean removed = false;
Entry[] tab = table;
int len = tab.length;
do {
// from i To traverse the
i = nextIndex(i, len);
Entry e = tab[i];
// Found Entry Not empty , however key by null Of Entry
if (e != null && e.get() == null) {
n = len; // Reset n
removed = true; // Element remove identity
i = expungeStaleEntry(i); //
}
} while ( (n >>>= 1) != 0);
return removed;
}
expungeStaleEntry(int staleSlot)
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// expunge entry at staleSlot
tab[staleSlot].value = null; // On the array staleSlot Empty object for position
tab[staleSlot] = null;
size--; // Reduce the number of arrays 1
// Rehash until we encounter null
Entry e;
int i;
// staleSlot Continue traversing
for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
// If you find key Empty , Also empty
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
// Current Entry Of key Not empty
// Recalculate the Entry Index position of
int h = k.threadLocalHashCode & (len - 1);
// If the index location is not the current index location i
if (h != i) {
// take i Position empty
tab[i] = null;
// If at this time h If there are other elements in the location, continue to find the appropriate location , take e place .
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
return i;
}
Update and expand
private void rehash() {
// call expungeStaleEntries Method ( The method and expungeStaleEntry similar , Just expand the search scope to the entire table ) clear key Empty Entry
expungeStaleEntries();
// If after cleaning size Is a threshold value 3/4, namely size Of 1/2, Then expand the capacity .
if (size >= threshold - threshold / 4)
resize();
}
private void resize() {
Entry[] oldTab = table;
int oldLen = oldTab.length;
// The new length is the old one 2 times
int newLen = oldLen * 2;
Entry[] newTab = new Entry[newLen];
// Number of elements processed , Number of valid elements
int count = 0;
// Traverse all elements
for (int j = 0; j < oldLen; ++j) {
Entry e = oldTab[j];
if (e != null) {
ThreadLocal<?> k = e.get();
// if key It's empty , Will be for value Also set null
if (k == null) {
e.value = null; // Help the GC
} else {
// by e Calculate the index position of the new table
int h = k.threadLocalHashCode & (newLen - 1);
// h The position of may have value , Just look for the right place again
while (newTab[h] != null)
h = nextIndex(h, newLen);
// Put the element e Put it in h Location
newTab[h] = e;
count++;
}
}
}
// Set the threshold for new table expansion
setThreshold(newLen);
// to update size
size = count;
// to update table
table = newTab;
}
createMap(Thread t, T firstValue)
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
get process analysis
get()
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;
}
}
return setInitialValue();
}
Follow
setIn the same way , Get the current thread first , ReusegetMapMethod to get thethreadLocalsVariableIf
threadLocalsNot empty , Will callgetMethodicalThreadLocalAskey, callgetEntryMethod to find the correspondingEntry.If
threadLocalsEmpty or target not foundEntry, CallsetInitialValueMethod to initialize .
setInitialValue()
And set Methods have only initialization differences
private T setInitialValue() {
T value = initialValue(); // The default is null
Thread t = Thread.currentThread();
// Get the current thread's `threadLocals` Variable
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
getEntry(ThreadLocal<?> key)
private Entry getEntry(ThreadLocal<?> key) {
// Calculation key For the index position of
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
// If we find the corresponding key With the incoming key equal , Then return to .
if (e != null && e.get() == key)
return e;
else
// if e by null perhaps e Corresponding key With the incoming key atypism , Then keep looking
return getEntryAfterMiss(key, i, e);
}
getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e)
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
// if e If it is not empty, continue to traverse to find
while (e != null) {
ThreadLocal<?> k = e.get();
// Find the target Entry Then return to .
if (k == key)
return e;
// If you find key by null, The clear logic is triggered
if (k == null)
expungeStaleEntry(i);
else
// Find the next location , Get the corresponding Entry
i = nextIndex(i, len);
e = tab[i];
}
// If you can't find it null
return null;
}
remove process analysis
remove()
public void remove() {
// Gets the current thread's ThreadLocalMap
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
- Gets the current thread's threadLocals attribute , If it's not empty , Will key For the current ThreadLocal Key value pairs of are removed , At the same time, it will call
expungeStaleEntry()Method to remove key by null Of Entry.
remove(ThreadLocal<?> key)
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
// according to hashCode Calculate the current ThreadLocal Index position of
int i = key.threadLocalHashCode & (len-1);
// from i To traverse the , until Entry by null
for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
// Find the corresponding key
if (e.get() == key) {
// clear() Method will be cleared key References to
e.clear();
// from i Start clearing key by null The elements of
expungeStaleEntry(i);
return;
}
}
}
Memory leak
Memory leak (Memory Leak) It refers to the heap memory that has been dynamically allocated in the program. For some reason, the program is not released or cannot be released , Cause the waste of system memory , Causes the procedure to run the speed to slow down even the system crashes and so on the serious consequence .
* out of memory (Out Of Memory, abbreviation OOM)* It means that there is too much memory in the application system that cannot be recycled or used , Finally, the memory used for program running is larger than the maximum memory provided .
Excessive memory leaks can cause memory overflows .
ThreadLocalMap Use ThreadLocal As a weak reference to Entry Of key, If one ThreadLocal There is no external strong reference to it , Next system GC when , This ThreadLocal It's bound to be recycled , thus ,ThreadLocalMap Will appear in key by null Of Entry, There's no way to visit these key by null Of Entry Of value.
If the current thread has been running , And never execute get、set、remove Method , these key by null Of Entry Of value There will always be a strong reference chain :Thread Ref -> Thread -> ThreadLocalMap -> Entry -> value, Lead to these key by null Of Entry Of value Never recycle , Memory leak .
The total memory of a memory leak is In each thread key by null Of value The sum of .
How to avoid memory leaks
To avoid that , We can use it after ThreadLocal after , Manual call remove Method , To avoid memory leaks .
THREAD_LOCAL_1.remove();
THREAD_LOCAL_2.remove();
Use scenarios
Each in 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) (serialNum.get())).intValue();
}
}
session Management of
private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() throws InfrastructureException {
Session s = (Session) threadSession.get();
try {
if (s == null) {
s = getSessionFactory().openSession();
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}
Used in thread pool threadlocal
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadLocalDemo {
private static final ThreadLocal<String> THREAD_LOCAL_1 = ThreadLocal.withInitial(() -> "0s");
private static final ThreadLocal<Student> THREAD_LOCAL_2 = new ThreadLocal<>();
private static final CountDownLatch COUNT_DOWN_LATCH = new CountDownLatch(10);
private static final ThreadPoolExecutor POOL_EXECUTOR = initThreadPool(5, 10, 1000);
/** * The worker thread */
public static class WorkerThreadFactory implements ThreadFactory {
private final String namePrefix;
private final AtomicInteger nextId = new AtomicInteger(1);
WorkerThreadFactory(String whatFeatureOfGroup) {
this.namePrefix = "From WorkerThreadFactory's " + whatFeatureOfGroup + "-Worker-";
}
@Override
public Thread newThread(Runnable task) {
String name = namePrefix + nextId.getAndIncrement();
return new Thread(null, task, name, 0);
}
}
/** * Initializes the thread pool */
public static ThreadPoolExecutor initThreadPool(int corePoolSize, int maxPoolSize, long keepAliveTime) {
return new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000),
new WorkerThreadFactory("ThreadLocalDemo"),
new ThreadPoolExecutor.AbortPolicy());
}
static class Student {
int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
private static Student getStudent() {
Student s = THREAD_LOCAL_2.get();
if (null == s) {
s = new Student();
s.setAge(5);
THREAD_LOCAL_2.set(s);
}else {
s.setAge(55);
THREAD_LOCAL_2.set(s);
}
return s;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
int finalI = i + 1;
POOL_EXECUTOR.execute(() -> {
Student student = getStudent();
System.out.println("[" + new SimpleDateFormat("HH:mm:ss.SSS").format(new Date())
+ "]--[" + Thread.currentThread().getName() + "] THREAD_LOCAL_1, threadId:" + Thread.currentThread().getId() + ", " + THREAD_LOCAL_1.get());
System.out.println("[" + new SimpleDateFormat("HH:mm:ss.SSS").format(new Date())
+ "]--[" + Thread.currentThread().getName() + "] THREAD_LOCAL_2, threadId:" + Thread.currentThread().getId() + ", " + student.getAge());
THREAD_LOCAL_1.set("THREAD_LOCAL_1 is " + finalI);
System.out.println("[" + new SimpleDateFormat("HH:mm:ss.SSS").format(new Date())
+ "]--[" + Thread.currentThread().getName() + "] THREAD_LOCAL_1, threadId:" + Thread.currentThread().getId() + ", " + THREAD_LOCAL_1.get());
THREAD_LOCAL_1.remove();
COUNT_DOWN_LATCH.countDown();
});
THREAD_LOCAL_2.remove();
}
if (COUNT_DOWN_LATCH.await(2, TimeUnit.MINUTES)) {
System.out.println("[" + new SimpleDateFormat("HH:mm:ss.SSS").format(new Date()) + "]--[" + Thread.currentThread().getName() + "] is over.");
}
POOL_EXECUTOR.shutdown();
}
}
Reference article
https://zhuanlan.zhihu.com/p/34406557
边栏推荐
猜你喜欢

易快报:我们用 Zadig 实现万次构建部署,聪明运维,释放开发生产力
![Jerry's initiation of ear pairing, reconnection, and opening of discoverable and connectable cycle functions [chapter]](/img/d7/f73e748ada302440326a8b1a46f916.png)
Jerry's initiation of ear pairing, reconnection, and opening of discoverable and connectable cycle functions [chapter]

When you are young, you should be awake to fight, and when you are young, you should have the courage to try
![Jerry's about TWS pairing mode configuration [chapter]](/img/c8/d78e817295169753244299545d9aba.png)
Jerry's about TWS pairing mode configuration [chapter]

Interpolated scatter data

Dragon Book tiger Book whale Book gnawing? Try the monkey book with Douban score of 9.5

&3 在浏览器中查看请求报文和响应报文

速看|期待已久的2022年广州助理检测工程师真题解析终于出炉

爱可可AI前沿推介(6.29)

Jericho's position on initiating the connection back to the opposite ear: 【 chapter 】
随机推荐
妙!妙盈科技全面实施 Zadig 助力容器化建设,全面拥抱 Kubernetes 和云原生
龙书虎书鲸书啃不动?试试豆瓣评分9.5的猴书
Unified exception reporting practice based on bytecode
内插散点数据
这个EMR-SparkSQL节点,他查询的表是不是ODPS的啊?
MATLAB Gui 实现点击按钮,打开文件对话框,导入图片功能
每周推荐短视频:爱因斯坦是怎样思考问题的?
GBase8s数据库select有ORDER BY 子句4
GBase8s数据库FOR UPDATE 子句
oracle 19c : change the user sys/system username pasword under Linux
ERP preparation of bill of materials Huaxia
在校生的编程故事
Gbase8s database sorts standard or raw result tables
论文复现——AC-FPN:Attention-guided Context Feature Pyramid Network for Object Detection.
MySQL 主从复制原理以及流程
GBase8s数据库INTO TEMP 子句创建临时表来保存查询结果。
GBase 8s 扩展外连接1
Jericho's position on initiating the connection back to the opposite ear: 【 chapter 】
[pbootcms template] composition website / document download website source code
Jerry's about TWS pairing mode configuration [chapter]