- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在我的代码中,我想测试ThreadLocal的GC策略。我使用两种方法。一种是ThreadPool
,另一种是自创建的线程。在第一种情况下,JVM
似乎不会 GC Thread 的 ThreadLocalMap
(没有 finalize()
输出)。另一个效果很好。
我发现了。 2007 年 10 月,Josh Bloch(与 Doug Lea 共同开发 java.lang.ThreadLocal)写道:
"The use of thread pools demands extreme care. Sloppy use of thread pools in combination with sloppy use of thread locals can cause unintended object retention, as has been noted in many places."
我猜 ThreadPool
使用 ThreadLocal
可能很危险。
这是我的代码(JDK8环境)
public class ThreadLocalDemo_Gc {
static volatile ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>(){
// overwrite finalize, such that the message will be printed when GC happens.
protected void finalize() throws Throwable{
System.out.println(this.toString() + " is gc(threadlocal)");
}
};
// Let the main thread wait for all workers.
static volatile CountDownLatch cd = new CountDownLatch(10);
public static class ParseDate implements Runnable{
int i = 0;
public ParseDate(int i) {
super();
this.i = i;
}
@Override
public void run() {
try {
if(tl.get() == null){
tl.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"){
// overwrite finalize, such that the message will be printed when GC happens.
protected void finalize() throws Throwable {
System.out.println(this.toString() + " is gc(sdf)");
}
});
// new sdf object is created in ThreadLocalMap
System.out.println(Thread.currentThread().getId() + ":create SimpleDateFormat");
}
Date t = tl.get().parse("2017-3-26 17:03:" + i % 60);
} catch (ParseException e) {
e.printStackTrace();
} finally {
cd.countDown();
}
}
}
// code with ThreadPool
// public static void main(String[] args) throws InterruptedException {
// ExecutorService es = Executors.newFixedThreadPool(10);
//
// for(int i = 0; i < 10; i++){
// es.execute(new ParseDate(i));
// }
// cd.await();
//
// System.out.println("mission complete");
//
// tl = null; // free the weak reference
// System.gc();
// System.out.println("First GC complete");
// es.shutdown();
// }
// not pooling threads
public static void main(String[] args) throws InterruptedException {
Thread[] all = new Thread[10];
for(int i = 0; i < 10; i++){
all[i] = new Thread(new ParseDate(i));
}
for(int i = 0; i < 10; i++){
all[i].start();
}
cd.await();
tl = null;
System.gc();
System.out.println("First GC complete");
}
}
运行第一个 main()
函数后。所有 SimpleDateFormat
对象都未被 GC 处理。第二个 main()
函数确实完成了这项工作。
编辑#1感谢格雷的提醒。导致finalize()
函数没有输出的真正问题是ThreadPool
可能没有被真正收集。在测试代码中,仅使用了shutdown()
。但是,在此过程之后可能不会收集工作线程。因此更安全的方法是调用 awaitTermination()
。该函数确实生成所有工作线程实例,并收集这些线程所属的资源,特别是ThreadLocalMap
。
这里是 main()
与 ThreadPool
的修订版
// code with ThreadPool
public static void main(String[] args) throws InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(10);
for(int i = 0; i < 10; i++){
es.execute(new ParseDate(i));
}
cd.await();
es.shutdown();
es.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
System.gc();
}
此版本的 main()
运行良好,打印了来自 finalize()
方法的所有集合消息。
最后,当Entry
的键的实例没有稳定的引用时,Java GC可能不会收集值。由于ThreadLocalMap
的键是弱引用,因此Entry
的键变为null
。但是,Entry
的值不会被GC。这个结论或许可以在我的测试中得到证实。
最佳答案
I guess ThreadPool may be dangerous to use ThreadLocal.
我不会走这么远。我想说你需要考虑到 ThreadLocal
除非线程本身终止,否则不会回收存储。
但是在查看您的测试代码时,ExecutorService
都存在很多问题。和直接线程主方法。在这两种情况下,您都没有正确连接已完成的线程。抛弃CountDownLatch
并在 gc()
之前执行以下操作调用:
for (int i = 0; i < 10; i++) {
all[i].join();
}
或
es.shutdown();
es.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
但是代码的真正问题是 Finalizer 线程存在竞争条件。 gc 线程完成,但对象的实际终结发生在 GC 完成后的另一个“Finalizer”线程中。如果你只是在 main()
的末尾放置 1 秒 sleep 您应该会看到所有 10 个 SDF 均已收获。
这真正说明的是,以这种方式强制对象进行 GC 是很困难的。推杆System.out.println(...)
finalizer()
中的命令即使我知道你这样做是为了了解更多关于 ThreadLocal
的信息,但一想到它就让我感到不寒而栗。的内存使用情况。
我认为将东西存储在ThreadLocal
中如果仔细做应该不会有问题。在你线程的 run 方法中,我只需执行 try / finally
阻止并确保执行 threadLocal.remove()
在 finally
因此线程在退出之前会自行清理。但如果我有一个在应用程序的生命周期中运行的后台线程,我什至不会为此烦恼。实际上,您需要特别担心的只是来来去去的线程。
最后,不需要 ThreadLocal
字段为volatile
它应该是 static
ParseDate
内如果可能的话。
希望这有帮助。
关于java - Java ThreadPool中ThreadLocal值是否被GC?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43028388/
我在我的应用程序中使用 Windows ThreadPools,每次调用 CreateThreadPoolWork() 时都会遇到 136 字节的内存泄漏,如通过 UMDH 所见: + 1257728
摘自对我之前一个问题的回答( Task.Factory.StartNew starts with a great delay despite having available threads in t
这有什么区别?请引用选项1和选项2。因为我遇到了麻烦,因为它们好像是一样的。它们运行正确 Thread ThreadPoolExecutor executor = (ThreadPoolExecuto
Windows Phone 8 SDK 文档没有描述 Windows.System.Threading.ThreadPool 之间的区别。和 System.Threading.ThreadPool .
我无法通过JSP使用Elasticsearch Java API。在下面,我试图解释我所做的事情。 :| 我已经按照 flex 指令在系统上安装了elasticseach 2.3.3,并在命令提示符下
我试图在 Thread 的帮助下找出控制台应用程序中运行的线程数: new Thread(() => { while (true) {
我们对来自 Android 应用程序的所有网络流量使用 Retrofit/OkHttp3。到目前为止,一切似乎都很顺利。 但是,我们现在偶尔会遇到我们的应用程序/进程用完文件句柄的情况。 Androi
我们应该为长时间运行的线程使用线程池还是启动我们自己的线程?有什么设计模式吗? 最佳答案 不幸的是,这取决于。没有硬性规定说您应该始终使用线程池。 线程池提供两个主要功能: 线程的委托(delegat
我只是实现了一个线程池,如这里所述 Allen Bauer on thread pools 非常简单的实现,可以正常工作,但是我的应用程序不再关闭。似乎有两个工作线程(和另一个线程,我想是排队线程)卡
我有以下代码: static void Main(string[] args) { Console.Write("Press ENTER to start..."); Console.
我在下面编写了一个示例程序。 class Program { static int x = 2; static void Main(string[] args)
我试图了解Parralel.For和ThreadPool.QueueUserWorkItem之间的区别。 硬件和软件: 英特尔i5(四核) Windows 7 64位教授 DotNet 4.5 案例1
当用户单击按钮时,我使用ThreadPool.QueueUserWorkItem生成了一个启动长时间运行的线程的线程。我想在线程完成时使按钮可见,以便用户可以单击它。 ThreadPool中是否有已完
我想要一个类似的功能: public static V callAsyncAndWait(Func func) { ThreadPool.QueueUserWorkItem(obj =>
我正在尝试编写一个 Java 多线程程序,对作为文件给出的 2 个矩阵执行乘法,并使用有限的线程总数。 例如,如果我将线程数设置为 16,我希望我的线程池能够重用这 16 个线程,直到所有任务完成。
我有一个 C# 控制台应用程序,其中有一个线程池。在线程池中会有一个类执行一个连续的方法(直到它运行了一段时间或者知道什么时候停止)。该方法连接到 HttpWebResponse 流并继续读取它。 问
我正在使用 Reflector 仔细阅读 .Net ThreadPool 的一些源代码,当它显示以下内容时: private static bool QueueUserWorkItemHelper(W
每当用户扫描条形码时,我都会触发一个线程。 大多数时候它是一个相当短的运行线程。但有时可能需要很长时间(等待调用 GUI 线程)。 我读到过,为此使用 ThreadPool 可能是个好主意,而不是为每
我有一个场景,我试图通过在可能的情况下在用户实际需要之前预取结果的一些子元素来转变为响应速度更快的 UI。我不清楚如何最好地处理线程,所以我希望有人可以提供一些建议。 场景 有一个搜索表单(.NET
我需要对事件进行排队并并行执行它们。 我的 C# 代码在阻塞集合中对事件进行排队,并使用 ThreadPool 在工作线程上执行每个事件。但是,如果事件以 2000 事件/秒或更高的速率排队,则会错过
我是一名优秀的程序员,十分优秀!