在业务开发中,threadLocal有两种业务场景运用较多。
1、threadlocal保证每个线程独享对象,创建本地线程的副本,保证每个线程都拥有属于自己的副本,这样保证线程安全。
2、threadlocal可以作用类似全局变量,在其中一个方法里用set存入数据,其他方法可以用get直接获取,避免了传参,作用到每个线程独立保存信息。
场景一:
//private static final Integer NUM = 0;
private static final ThreadLocal<Integer> NUM = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 0;
}
};
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
Thread thread = new Thread() {
@Override
public void run() {
add();
}
};
thread.start();
}
}
private static void add() {
for (int i = 0; i < 5; i++) {
Integer n = NUM.get();
n++;
NUM.set(n);
System.out.println("当前线程:" + Thread.currentThread().getName() + "的[NUM]=" + n);
}
}
结果:开启了三个线程,因为用了threadlocal,每个线程都有属于自己的本地副本,并不会累加到15,而是n最大输出5。
还有就是simpledateformat也适用于场景一,当线程池开启,提交10000个或者更多任务时,每个线程都创建属于自己的simpledateformat开销会很大,而且占用内存,销毁对象也是有开销的:这种情况下,我们首先想到的是所有线程先共用一个simpledataformate,但他不是线程安全的,如何解决呢,给他加synchrolized锁,这样可以解决,但线程运行的时候,会发生阻塞,非常影响效率,此时此刻threadlocal就是非常好的解决办法。
threadlocal会让每个线程的simpledateformat独立,如果线程池Executors.newFixedThreadPool(15)开启15个线程,则只会创建15个simpledateformat,而且线程安全,相互之前不会影响。
场景二:
//保证对象取出,不需要传参
public static void main(String[] args) {
Dog dog = new Dog("小白");
new Service1().service1(dog);
}
static class Dog {
String name;
public Dog(String name) {
this.name = name;
}
}
static class DogThread {
//创建ThreadLocal保存User对象
public static ThreadLocal<Dog> dogThreadLocal = new ThreadLocal<>();
}
static class Service1 {
public void service1(Dog dog) {
//赋值
DogThread.dogThreadLocal.set(dog);
new Service2().Service2();
}
}
static class Service2 {
public void Service2() {
//取值
System.out.println("service2:" + DogThread.dogThreadLocal.get().name);
//记得移除
DogThread.dogThreadLocal.remove();
}
}
场景二类似于全局变量,免去了传参的麻烦 ,保证方法得到的对象值相同,先将对象存储在localthread里面,然后在获取,最后一定要记得remove掉。
问:线程池中使用threadlocal为什么容易造成内存泄漏?
因为线程池里的线程存活周期太长,往往跟程序的停止才销毁的,这样threadlocal持有的threadlocalmap就一直不会被回收,解决办法,可以手动在finally块调用它的remove()方法。