Java中的ThreadLocal詳解
一、ThreadLocal
1、說明
ThreadLocal 是一個線程局部變量。其實的功用非常簡單,就是為每一個使用該變量的線程都提供一個變量值的副本,是Java中一種較為特殊的線程綁定機制,是每一個線程都可以獨立地改變自己的副本,而不會和其它線程的副本沖突。
2、使用方法
ThreadLocal<String> nameThreadLocal = new ThreadLocal<>(); nameThreadLocal.set(name); nameThreadLocal.get();
3、Request 使用案例
- 在controller中注入的request是jdk動態(tài)代理對象,ObjectFactoryDelegatingInvocationHandler的實例.當(dāng)我們調(diào)用成員域request的方法的時候其實是調(diào)用了objectFactory的getObject()對象的相關(guān)方法.這里的objectFactory是RequestObjectFactory
- RequestObjectFactory的getObject其實是從RequestContextHolder的threadlocal中去取值的
- 請求剛進入springmvc的dispatcherServlet的時候會把request相關(guān)對象設(shè)置到RequestContextHolder的threadlocal中去
二、InheritableThreadLocal
1、說明
ThreadLocal設(shè)計之初就是為了綁定當(dāng)前線程,如果希望當(dāng)前線程的ThreadLocal能夠被子線程使用,實現(xiàn)方式就會相當(dāng)困難(需要用戶自己在代碼中傳遞)。在此背景下,InheritableThreadLocal應(yīng)運而生。
2、原理
創(chuàng)建新的線程時,會調(diào)用以下的構(gòu)造方法。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}我們沿著構(gòu)造方法中的init方法,可以找到這樣一段代碼。就是在這里創(chuàng)建的 inheritableThreadLocals 。
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);繼續(xù)查看 createInheritedMap 方法,里面新建了一個 ThreadLocalMap 對象,其構(gòu)造方法如下。
分析代碼我們可知,在創(chuàng)建子線程時,會將父線程的 inheritableThreadLocals 復(fù)制到子線程的 inheritableThreadLocals 對象。
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}三、TransmittableThreadLocal
1、概述
使用線程池時,線程會被復(fù)用,因此線程池中的線程,執(zhí)行任務(wù)時如果要獲取 提交線程(即 提交任務(wù)到線程池 的線程) 保存的對象,則可以使用 TransmittableThreadLocal 。
2、使用方法
TransmittableThreadLocal<String> ttl = new TransmittableThreadLocal<>();
ThreadPoolExecutor executor = new ThreadPoolExecutor(ThreadSize, ThreadSize, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(10));
// 設(shè)置變量,并將任務(wù)提交到線程池
ttl.set("ThreadA-TTL");
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println(ttl.get());
}
};
// 生成修飾后的對象ttlRunnable。
task = TtlRunnable.get(task);
executor.submit(task);3、案例(業(yè)務(wù)侵入式)
(1)代碼
package com.scy.example.controller;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.TtlRunnable;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Test02 {
static ThreadPoolExecutor executor = null;
static TransmittableThreadLocal<String> ttl = new TransmittableThreadLocal<>();
public static void main(String[] args) {
try {
// 在A線程中創(chuàng)建線程池,并且啟動線程池中的線程
Thread threadA = new Thread(() -> {
int ThreadSize = 1;
executor = new ThreadPoolExecutor(ThreadSize, ThreadSize, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(10));
ttl.set("ThreadA-TTL");
// 啟動線程池中的線程
for (int i = 0; i < ThreadSize; i++) {
executor.submit(() -> {
System.out.println("初始化線程完畢!");
});
}
});
threadA.start();
// 等待線程池中的線程啟動完畢
Thread.sleep(100);
// 在B線程中,向線程池提交任務(wù)
Thread threadB = new Thread(() -> {
ttl.set("ThreadB-TTL");
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println(ttl.get());
}
};
// 額外的處理,生成修飾了的對象ttlRunnable
task = TtlRunnable.get(task);
executor.submit(task);
});
threadB.start();
Thread.sleep(100);
executor.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}(2)結(jié)果
初始化線程完畢!
ThreadB-TTL
(3)說明
使用 TransmittableThreadLocal 存儲對象時;
如果任務(wù)沒有使用 TtlRunnable 修飾,則 TransmittableThreadLocal 相當(dāng)于 InheritableThreadLocal ;此時在線程池中執(zhí)行任務(wù)時獲取對象,獲取的是 創(chuàng)建線程(即 創(chuàng)建線程池中線程 的線程) 保存的對象。
如果任務(wù)使用了 ttlRunnable 修飾,此時在線程池中執(zhí)行任務(wù)時獲取對象,獲取的是 提交線程(即 提交任務(wù)到線程池 的線程) 保存的對象。而且無法獲取創(chuàng)建線程保存的對象。
4、案例(agent實現(xiàn))
需配置啟動參數(shù) -javaagent:path/to/transmittable-thread-local-2.x.x.jar
-javaagent:D:\maven\mvnRespo\com\alibaba\transmittable-thread-local\2.13.2\transmittable-thread-local-2.13.2.jar

四、TaskDecorator
1、概述
使用線程池時,線程會被復(fù)用,因此線程池中的線程,執(zhí)行任務(wù)時如果要獲取 提交線程(即 提交任務(wù)到線程池 的線程) 保存的對象,還可以使用 TaskDecorator 。
看這個名稱大概就能猜出是一個裝飾器設(shè)計原理。
spring 4.3 提供 TaskDecorator
2、使用方法
主線程16個,子線程2個,執(zhí)行10次,目的是盡可能讓子線程復(fù)用。
public class TaskDecoratorTest {
public static void main(String[] args) {
new TaskDecoratorTest().testThreadLocal();
}
public void testThreadLocal() {
ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
ExecutorService mainThreadPool = Executors.newFixedThreadPool(16);
ThreadPoolTaskExecutor childThreadPool = new ThreadPoolTaskExecutor();
childThreadPool.setCorePoolSize(2);
childThreadPool.setMaxPoolSize(2);
childThreadPool.setTaskDecorator(runnable -> {
int v = threadLocal.get();
System.out.println("裝飾器中獲取到主線程=" + Thread.currentThread().getName() + " 獲取上下文=" + v);
return () -> {
try {
//重新copy傳遞給子線程
threadLocal.set(v);
runnable.run();
} finally {
threadLocal.remove();
}
};
});
childThreadPool.initialize();
for (int i = 0; i < 10; i++) {
int finalI = i;
mainThreadPool.execute(() -> {
//模擬在主線程設(shè)置上下文變量
threadLocal.set(finalI);
childThreadPool.execute(() -> System.out.println("子線程" + Thread.currentThread().getName() + " 獲取上下文變量=" + threadLocal.get()));
threadLocal.remove();
});
}
try {
childThreadPool.getThreadPoolExecutor().awaitTermination(3, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}3、原理
@Override
protected ExecutorService initializeExecutor(
ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);
ThreadPoolExecutor executor;
//當(dāng)線程池的裝飾器不為空時,執(zhí)行execute方法會進入這里,因為它重寫了execute方法
if (this.taskDecorator != null) {
executor = new ThreadPoolExecutor(
this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
queue, threadFactory, rejectedExecutionHandler) {
//這里是一個代理設(shè)計模式的實現(xiàn),對execute做了一層代理
@Override
public void execute(Runnable command) {
//執(zhí)行裝飾器的邏輯,注意這段代碼是在主線程中運行
Runnable decorated = taskDecorator.decorate(command);
if (decorated != command) {
decoratedTaskMap.put(decorated, command);
}
//子線程真正執(zhí)行的方法(異步模塊)...初始化核心線程數(shù),核心線程滿了入隊列,隊列滿開啟至最大線程數(shù)
super.execute(decorated);
}
};
}
else {
executor = new ThreadPoolExecutor(
this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
queue, threadFactory, rejectedExecutionHandler);
}
if (this.allowCoreThreadTimeOut) {
executor.allowCoreThreadTimeOut(true);
}
this.threadPoolExecutor = executor;
return executor;
}到此這篇關(guān)于Java中的ThreadLocal詳解的文章就介紹到這了,更多相關(guān)ThreadLocal詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot默認(rèn)使用HikariDataSource數(shù)據(jù)源方式
這篇文章主要介紹了SpringBoot默認(rèn)使用HikariDataSource數(shù)據(jù)源方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
JAVA錯誤:'無效目標(biāo)發(fā)行版?17'的解決方案
這篇文章主要給大家介紹了關(guān)于JAVA錯誤:'無效目標(biāo)發(fā)行版?17'的解決方案,文中通過圖文介紹的非常詳細(xì),對大家學(xué)習(xí)或使用java具有一的的參考學(xué)習(xí)價值,需要的朋友可以參考下2022-09-09
springboot?@Validated的概念及示例實戰(zhàn)
這篇文章主要介紹了springboot?@Validated的概念以及實戰(zhàn),使用?@Validated?注解,Spring?Boot?應(yīng)用可以有效地實現(xiàn)輸入驗證,提高數(shù)據(jù)的準(zhǔn)確性和應(yīng)用的安全性,本文結(jié)合實例給大家講解的非常詳細(xì),需要的朋友可以參考下2024-04-04
圖解Java?ReentrantLock公平鎖和非公平鎖的實現(xiàn)
ReentrantLock是Java并發(fā)中十分常用的一個類,具備類似synchronized鎖的作用。但是相比synchronized,?它具備更強的能力,同時支持公平鎖和非公平鎖。本文就來聊聊ReentrantLock公平鎖和非公平鎖的實現(xiàn),需要的可以參考一下2022-10-10
Java中Word與PDF轉(zhuǎn)換為圖片的方法詳解
這篇文章主要為大家詳細(xì)介紹了如何使用Java實現(xiàn)將Word與PDF轉(zhuǎn)換為圖片,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-10-10
SpringMVC整合SpringSession 實現(xiàn)sessiong
這篇文章主要介紹了SpringMVC整合SpringSession 實現(xiàn)session的實例代碼,本文通過實例相結(jié)合的形式給大家介紹的非常詳細(xì),需要的朋友參考下吧2018-04-04

