我看到有人说:“当你想在课堂上使用ThreadLocal时,请以静态方式使用它”,例如:
private static ThreadLocal<SimpleDateFormat> dayFormat =
new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
我不确定为什么这可以避免内存泄漏。有人可以澄清一下吗?
答案 0 :(得分:2)
我编写了一些PoC并启动jvisualvm
来实际显示静态ThreadLocal
是否与实例ThreadLocal
有任何不同。
代码启动了10个不同的线程,每个线程运行一个亿次迭代循环,利用SimpleDateFormat
字段中存储的ThreadLocal
对象。
ThreadLocal
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " " + dayFormat.get().format(new Date()));
for (int j = 0; j < 100000000; j++) {
dayFormat.get().format(new Date());
}
System.out.println(Thread.currentThread().getName()+" "+dayFormat.get().format(new Date()));
}).start();
}
}
private static ThreadLocal<SimpleDateFormat> dayFormat =
new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
}
此处没有内存泄漏,正如预期的那样。
ThreadLocal
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
Main m = new Main();
System.out.println(Thread.currentThread().getName() + " " + m.dayFormat.get().format(new Date()));
for (int j = 0; j < 100000000; j++) {
m.dayFormat.get().format(new Date());
}
System.out.println(Thread.currentThread().getName()+" "+m.dayFormat.get().format(new Date()));
}).start();
}
}
private ThreadLocal<SimpleDateFormat> dayFormat =
new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
}
这里也没有内存泄漏。它的运行速度比静态版本快得多(约20%),但内存占用量差别不大。
然而,由于该代码从来没有&#34; nullify&#34; ThreadLocal
对该对象的引用,我们不知道GC对这一切的感受。
以下代码可以。
ThreadLocal
,在运行时修改public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " " + dayFormat.get().format(new Date()));
for (int j = 0; j < 100000000; j++) {
dayFormat.set(null);
dayFormat.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
dayFormat.get().format(new Date());
}
System.out.println(Thread.currentThread().getName()+" "+dayFormat.get().format(new Date()));
}).start();
}
}
private static ThreadLocal<SimpleDateFormat> dayFormat =
new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
}
ThreadLocal
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
Main m = new Main();
System.out.println(Thread.currentThread().getName() + " " + m.dayFormat.get().format(new Date()));
for (int j = 0; j < 100000000; j++) {
m.dayFormat.set(null);
m.dayFormat.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
m.dayFormat.get().format(new Date());
}
System.out.println(Thread.currentThread().getName()+" "+m.dayFormat.get().format(new Date()));
}).start();
}
}
private ThreadLocal<SimpleDateFormat> dayFormat =
new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
}
在这两种情况下,内存占用率再次非常相似。这一次在GC中出现了某种停顿,这导致了一个&#34;凸起&#34;在内存图中,它既出现在静态版本中,也出现在实例版本中 运行时间有点类似,这次实例版本的速度稍慢(约5%)。
因此,正如大多数人所期望的那样,如果您将ThreadLocal
个对象声明为实例字段而不是静态字段,那么似乎没有任何内存泄漏风险