一、ThreadLocal配合线程池
注意配合remove方法,线程池是对线程进行复用的,如果没有及时的清理,那么之前对该线程的使用,就会影响到后面的线程了,造成数据不准确。
package com.test.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadLocalAndPool {
private static ThreadLocal<Integer> variableLocal = ThreadLocal.withInitial(() -> 0);
public static int get() {
return variableLocal.get();
}
public static void remove() {
variableLocal.remove();
}
public static void increment() {
variableLocal.set(variableLocal.get() + 1);
}
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(2,2,0, TimeUnit.SECONDS,new LinkedBlockingDeque<>(12));
for(int i=0;i<5;i++){
executorService.execute(()->{
long threadId = Thread.currentThread().getId();
String threadName = Thread.currentThread().getName();
int before = get();
increment();
int after = get();
System.out.println("threadid:" + threadId +" threadName:"+threadName +" before:" + before + " after:" + after);
/* threadlocal与线程池使用的问题了,因为threadlocal维护是 Map<Thread,T>这个结构。
* 而线程池是对线程进行复用的,如果没有及时的清理,那么之前对该线程的使用,就会影响到后面的线程了,造成数据不准确。
* */
remove();//增加remove,解决内存泄露
});
}
executorService.shutdown();
}
}
注释remove()方法结果
未注释remove()方法结果
二、线程池如何传递ThreadLocal
如何解决线程池中的值传递功能?
多个线程之间竞争同一个变量,为了线程安全进行值隔离,可以使用ThreadLocal。
父子线程之间的值传递,可以使用InheritableThreadLocal类来实现。
在遇到线程池等会池化复用线程的执行组件情况下,上述两种方案都会失灵,就需要通过TransmittableThreadLocal类来实现。
采用InheritableThreadLocal实现。阿里开发出来的TransmittableThreadLocal来让线程池提交任务时进行ThreadLocal的值传递。
2.1 maven依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.12.1</version>
</dependency>
2.2 JDK对跨线程传递ThreadLocal的支持
package com.test1;
public class TestThreadLocal {
public static void main(String[] args) {
/* 代码块1
ThreadLocal<Object> threadLocal = new ThreadLocal<>();
threadLocal.set("father");
new Thread(() -> {
System.out.println("subThread:" +threadLocal.get());
}).start();
*/
//代码块2
InheritableThreadLocal<String> itl = new InheritableThreadLocal<>();
itl.set("father");
new Thread(()->{
System.out.println("subThread:" + itl.get());//子线程可以拿到父线程的变量
itl.set("son");
System.out.println(itl.get());//子线程修改
}).start();
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//等待子线程执行完
System.out.println("thread:" + itl.get()); //子线程修改不影响父线程的变量
}
}
执行代码块1,则获取结果为null。执行代码块2结果
2.3 InheritableThreadLocal的实现原理
InheritableThreadLocal的实现原理,其实特别简单。就是Thread类里面分开记录了ThreadLocal、InheritableThreadLocal的ThreadLocalMap,初始化的时候,会拿到parent.InheritableThreadLocal。直接上代码可以看的很清楚。
class Thread {
...
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
...
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}
JDK的InheritableThreadLocal类可以完成父线程到子线程的值传递。但对于使用线程池等会池化复用线程的执行组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的ThreadLocal值传递已经没有意义,应用需要的实际上是把任务提交给线程池时的ThreadLocal值传递到任务执行时。
2.4 线程池场景下传值
package com.test1;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class InheritableThreadLocal1 {
static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,
3,
0,
TimeUnit.SECONDS,
new SynchronousQueue<>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) throws InterruptedException {
InheritableThreadLocal<Object> threadLocal = new InheritableThreadLocal<>();
threadLocal.set(5);
for(int i = 0; i < 3; i++){
threadPoolExecutor.submit(() ->{
System.out.println("threadlocal的值:"+threadLocal.get());
});
}
threadLocal.remove();
Thread.sleep(1000);
threadLocal.set(3);
for(int i = 0; i < 3; i++){
threadPoolExecutor.submit(() ->{
System.out.println("threadlocal的值:"+threadLocal.get());
});
}
threadLocal.remove();
}
}
如果不加控制,直接在线程池下传值,数据是失败,结果
改进版代码如下
package com.test1;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;
public class InheritableThreadLocal2 {
static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,
3,
0,
TimeUnit.SECONDS,
new SynchronousQueue<>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) throws InterruptedException {
ExecutorService ttlExecutorService = TtlExecutors.getTtlExecutorService(threadPoolExecutor);
InheritableThreadLocal<Object> threadLocal = new TransmittableThreadLocal<>();
threadLocal.set(5);
for(int i = 0; i < 3; i++){
ttlExecutorService.submit(() ->{
System.out.println("threadlocal的值:"+threadLocal.get());
});
}
threadLocal.remove();
Thread.sleep(1000);
threadLocal.set(3);
for(int i = 0; i < 3; i++){
ttlExecutorService.submit(() ->{
System.out.println("threadlocal的值:"+threadLocal.get());
});
}
threadLocal.remove();
}
}
执行结果
参考文章
https://www.jianshu.com/p/94ba4a918ff5
https://blog.csdn.net/weixin_38336658/article/details/105914390
https://blog.csdn.net/weixin_34080951/article/details/94632393
https://blog.csdn.net/weixin_41751625/article/details/116918284
https://blog.csdn.net/zyhui98/article/details/114459084
https://www.cnblogs.com/wudimanong/p/10457211.html
https://blog.csdn.net/yaomingyang/article/details/122350802