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的實例.當我們調用成員域request的方法的時候其實是調用了objectFactory的getObject()對象的相關方法.這里的objectFactory是RequestObjectFactory
- RequestObjectFactory的getObject其實是從RequestContextHolder的threadlocal中去取值的
- 請求剛進入springmvc的dispatcherServlet的時候會把request相關對象設置到RequestContextHolder的threadlocal中去
二、InheritableThreadLocal
1、說明
ThreadLocal設計之初就是為了綁定當前線程,如果希望當前線程的ThreadLocal能夠被子線程使用,實現(xiàn)方式就會相當困難(需要用戶自己在代碼中傳遞)。在此背景下,InheritableThreadLocal應運而生。
2、原理
創(chuàng)建新的線程時,會調用以下的構造方法。
public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); }
我們沿著構造方法中的init方法,可以找到這樣一段代碼。就是在這里創(chuàng)建的 inheritableThreadLocals 。
if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
繼續(xù)查看 createInheritedMap 方法,里面新建了一個 ThreadLocalMap 對象,其構造方法如下。
分析代碼我們可知,在創(chuàng)建子線程時,會將父線程的 inheritableThreadLocals 復制到子線程的 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、概述
使用線程池時,線程會被復用,因此線程池中的線程,執(zhí)行任務時如果要獲取 提交線程(即 提交任務到線程池 的線程) 保存的對象,則可以使用 TransmittableThreadLocal 。
2、使用方法
TransmittableThreadLocal<String> ttl = new TransmittableThreadLocal<>(); ThreadPoolExecutor executor = new ThreadPoolExecutor(ThreadSize, ThreadSize, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(10)); // 設置變量,并將任務提交到線程池 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è)務侵入式)
(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線程中,向線程池提交任務 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)結果
初始化線程完畢!
ThreadB-TTL
(3)說明
使用 TransmittableThreadLocal 存儲對象時;
如果任務沒有使用 TtlRunnable 修飾,則 TransmittableThreadLocal 相當于 InheritableThreadLocal ;此時在線程池中執(zhí)行任務時獲取對象,獲取的是 創(chuàng)建線程(即 創(chuàng)建線程池中線程 的線程) 保存的對象。
如果任務使用了 ttlRunnable 修飾,此時在線程池中執(zhí)行任務時獲取對象,獲取的是 提交線程(即 提交任務到線程池 的線程) 保存的對象。而且無法獲取創(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、概述
使用線程池時,線程會被復用,因此線程池中的線程,執(zhí)行任務時如果要獲取 提交線程(即 提交任務到線程池 的線程) 保存的對象,還可以使用 TaskDecorator 。
看這個名稱大概就能猜出是一個裝飾器設計原理。
spring 4.3 提供 TaskDecorator
2、使用方法
主線程16個,子線程2個,執(zhí)行10次,目的是盡可能讓子線程復用。
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(() -> { //模擬在主線程設置上下文變量 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; //當線程池的裝飾器不為空時,執(zhí)行execute方法會進入這里,因為它重寫了execute方法 if (this.taskDecorator != null) { executor = new ThreadPoolExecutor( this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler) { //這里是一個代理設計模式的實現(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; }
到此這篇關于Java中的ThreadLocal詳解的文章就介紹到這了,更多相關ThreadLocal詳解內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot默認使用HikariDataSource數(shù)據(jù)源方式
這篇文章主要介紹了SpringBoot默認使用HikariDataSource數(shù)據(jù)源方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10springboot?@Validated的概念及示例實戰(zhàn)
這篇文章主要介紹了springboot?@Validated的概念以及實戰(zhàn),使用?@Validated?注解,Spring?Boot?應用可以有效地實現(xiàn)輸入驗證,提高數(shù)據(jù)的準確性和應用的安全性,本文結合實例給大家講解的非常詳細,需要的朋友可以參考下2024-04-04圖解Java?ReentrantLock公平鎖和非公平鎖的實現(xiàn)
ReentrantLock是Java并發(fā)中十分常用的一個類,具備類似synchronized鎖的作用。但是相比synchronized,?它具備更強的能力,同時支持公平鎖和非公平鎖。本文就來聊聊ReentrantLock公平鎖和非公平鎖的實現(xiàn),需要的可以參考一下2022-10-10SpringMVC整合SpringSession 實現(xiàn)sessiong
這篇文章主要介紹了SpringMVC整合SpringSession 實現(xiàn)session的實例代碼,本文通過實例相結合的形式給大家介紹的非常詳細,需要的朋友參考下吧2018-04-04