在Java线程池中是否对ThreadLocal值进行GCed?
在我的代码中,我想测试ThreadLocal的GC策略。我使用两种方法。一个是在Java线程池中是否对ThreadLocal值进行GCed?,java,multithreading,garbage-collection,threadpoolexecutor,thread-local,Java,Multithreading,Garbage Collection,Threadpoolexecutor,Thread Local,在我的代码中,我想测试ThreadLocal的GC策略。我使用两种方法。一个是ThreadPool,另一个是自行创建的线程。在第一个场景中,JVM没有像GC线程的ThreadLocalMap那样(没有finalize()输出)。另一个效果很好 我找到了。2007年10月,Josh Bloch(java.lang.ThreadLocal与Doug Lea的合著者)写道: “线程池的使用需要非常小心。线程池的草率使用 池与随意使用线程局部变量相结合可能导致 在许多地方都注意到了意外的对象保留。” 我
ThreadPool
,另一个是自行创建的线程。在第一个场景中,JVM
没有像GC线程的ThreadLocalMap
那样(没有finalize()
输出)。另一个效果很好
我找到了。2007年10月,Josh Bloch(java.lang.ThreadLocal与Doug Lea的合著者)写道:
“线程池的使用需要非常小心。线程池的草率使用
池与随意使用线程局部变量相结合可能导致
在许多地方都注意到了意外的对象保留。”
我猜ThreadPool
使用ThreadLocal
可能很危险
这是我的代码(JDK8环境)
此版本的main()
运行良好,将打印来自finalize()
方法的所有收集消息
最后,当
Entry
的键的实例没有稳定的引用时,javagc可能不会收集值。由于ThreadLocalMap
的键是弱引用,Entry
的键变为null
。但是,条目
的值不是GCed。这个结论可以在我的测试中得到证实。线程本地本身的实例仅仅是存储在线程本身上的映射的视图。正在收集的实例实际上并不保证引用被切断
它可以近似为threadInstance.privateField=WeakHashMap
这意味着如果线程
实例变得不可访问,那么将成为线程本地
持有的所有关联值。另一方面,当ThreadLocal
实例变得不可访问时,这只意味着映射键为空(为弱引用),该值仍由映射保持活动状态,直到对映射的某些访问清除该值。映射清除是惰性执行的,因此清理ThreadLocal
引用与让线程终止的效果不同。
清理它的第三种方法是从线程内调用threadLocal.remove()
当然,在一个类中共享
静态final ThreadLocal tl
访问器是一种常见的模式。当与线程池结合使用时,这意味着这些值将与线程池一样保持活动状态,除非您使用remove()
ThreadLocal
本身的实例仅是存储在线程本身上的映射的视图。正在收集的实例实际上并不保证引用被切断
它可以近似为threadInstance.privateField=WeakHashMap
这意味着如果线程
实例变得不可访问,那么将成为线程本地
持有的所有关联值。另一方面,当ThreadLocal
实例变得不可访问时,这只意味着映射键为空(为弱引用),该值仍由映射保持活动状态,直到对映射的某些访问清除该值。映射清除是惰性执行的,因此清理ThreadLocal
引用与让线程终止的效果不同。
清理它的第三种方法是从线程内调用threadLocal.remove()
当然,在一个类中共享静态final ThreadLocal tl
访问器是一种常见的模式。当与线程池结合使用时,这意味着这些值将与线程池一样保持活动状态,除非您使用remove()
我猜ThreadPool使用ThreadLocal可能很危险
我不会走这么远。我想说,您需要考虑到,除非线程本身终止,否则不会获取ThreadLocal
存储
但是,在查看测试代码时,ExecutorService
和directthreadmain方法都存在很多问题。在这两种情况下,您都没有正确地连接已完成的线程。在调用gc()
之前,放弃倒计时闩锁,并执行以下操作:
for (int i = 0; i < 10; i++) {
all[i].join();
}
但代码的真正问题是,终结器线程存在竞争条件。gc线程完成,但对象的实际终结发生在gc完成后的另一个“终结器”线程中。如果您在main()
的末尾只睡1秒钟,您应该会看到所有10个SDF都被捕获
这实际上表明,很难以这种方式将对象强制到GC'd。将System.out.println(…)
命令放入finalizer()
会让我觉得很冷,即使我知道您这样做是为了了解ThreadLocal
的内存使用情况
我认为在ThreadLocal
s中存储东西,如果操作小心,应该不会有问题。在线程的run方法中,我只需执行一个try/finally
块,并确保在finally
中执行threadLocal.remove()
,以便线程在退出之前自行清理。但是,如果我有一个后台线程在我的应用程序的生命周期中运行,我甚至不会为此烦恼。实际上,你需要特别担心的只是来来往往的线程
最后,不需要将ThreadLocal
字段设置为volatile
,如果可能的话,它应该是ParseDate
中的static
希望这有帮助
我猜ThreadPool使用ThreadLocal可能很危险
我不会走这么远。我想说,您需要考虑到,除非线程本身终止,否则不会获取ThreadLocal
存储
但是,在查看测试代码时,ExecutorService
和directthreadmain方法都存在很多问题。在这两种情况下,你都没有正确地加入
// 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();
}
for (int i = 0; i < 10; i++) {
all[i].join();
}
es.shutdown();
es.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);