ThreadLocal 基本使用和内存泄漏分析,kafka 性能调优
// }
// };
public static void main(String[] args) {
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
System.out.println(threadLocal.get());
}
});
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
threadLocal.set(3);
System.out.println("t2:"+threadLocal.get());
}
});
t1.start();
t2.start();
System.out.println(threadLocal.get());
}
}
如果需要设置默认值的话,可以实现 initialValue 方法。
典型场景 1:我们知道 SimpleDateFormat 的对象如果多线程使用的话会有线程不安全的问题。具体代码如下:
public class TestThreadLocal {
public static ExecutorService executorService = Executors.newFixedThreadPool(16);
private static SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) throws InterruptedException {
for (int i=0;i<1000;i++){
executorService.submit(new Runnable() {
@Override
public void run() {
String format = simpleDateFormat.format(new Date());
try {
Date parse = simpleDateFormat.parse("2021-09-01 00:00:00");
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(format);
}
});
}
Thread.sleep(3000);
executorService.shutdownNow();
}
}
运行结果如下:
可以看出,发生了异常。
方法 1:我们可以改为每次都 new 一个新的 SimpleDateFormat 对象的话,这样再运行是没问题的。但是有些资源浪费。
方法 2:使用 ThreadLocal 来解决。假设线程池里共 16 个线程,那我们总共 16 个 SimpleDateFormat 对象就可以应付所有的日期格式化的调用。
代码如下:
public class TestThreadLocal {
public static ExecutorService executorService = Executors.newFixedThreadPool(16);
private static ThreadLocal<SimpleDateFormat> threadLocal=new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
private static SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) throws InterruptedException {
for (int i=0;i<1000;i++){
executorService.submit(new Runnable() {
@Override
public void run() {
String format = threadLocal.get().format(new Date());
try {
Date parse = threadLocal.get().parse("2021-09-01 00:00:00");
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(format);
}
});
}
Thread.sleep(3000);
executorService.shutdownNow();
}
}
注意:?如果不使用线程池,线程结束,线程里的 threadLocalMap 也会被回收。但是如果使用线程池,线程池里面的线程会被复用,线程里的 threadLocalMap 不会被回收,就造成了内存泄漏。按照正确的使用方法应该是每次用完了 remove,但是这样效率就很低。还不如方法 1 每次去 new 一个新的 SimpleDateFormat 对象。(但个人觉得其实还好,泄漏一点也没关系,不过 threadlocal 毕竟不是专门解决线程安全问题的,不推荐这么用)
评论