本文介绍了当类暴露给线程池时,它是否真的是我的工作清理ThreadLocal资源?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 我使用ThreadLocal 在我的Java类中,我有时使用 ThreadLocal 避免不必要的对象创建的方法: @ net.jcip.annotations.ThreadSafe public class DateSensitiveThing { private final日期; public DateSensitiveThing(Date then){ this.then = then; } private static final ThreadLocal< Calendar> threadCal = new ThreadLocal< Calendar>(){ @Override protected Calendar initialValue(){ return new GregorianCalendar(); } }; public Date doCalc(int n){ Calendar c = threadCal.get(); c.setTime(this.then): //使用n来改变c return c.getTime(); } } 我这样做是正确的原因 - GregorianCalendar 是那些光荣的有状态,可变,非线程安全的对象之一,它提供跨多个调用的服务,而不是表示一个值。此外,它被认为是昂贵的实例化(这是否是真的不是这个问题的要点)。 (总之,我真的很佩服它: - )) 如何Tomcat Whinges 在任何环境中的类,其中存在线程 - 和,其中我的应用程序不控制这些线程的生命周期 - 那么存在内存泄漏的可能性。一个Servlet环境是一个很好的例子。 事实上,Tomcat 7在webapp停止时会这样: SEVERE:web应用程序[]创建了一个ThreadLocal类型的键 [org.apache.xmlbeans.impl.store.CharUtil $ 1](value [org。 apache.xmlbeans.impl.store.CharUtil$1@2aace7a7])和值 type [java.lang.ref.SoftReference](值 [java.lang.ref.SoftReference@3d9c9ad4])但在 Web应用程序停止时无法删除它。线程将被更新超过的时间,以尝试,并避免可能的内存泄漏。 Dec 13,2012 12:54:30 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks 谁是责怪的? 这看起来不太公平。 Tomcat正在指责我(或我的类的用户)做正确的事情。 最终,这是因为Tomcat想要重用其为我提供的线程,用于其他网络应用程序。 (Ugh - 我觉得脏。)也许,这不是一个伟大的政策在Tomcat的一部分 - 因为线程实际上有/ cause状态 - 不共享的应用程序之间。 但是,这个策略至少是常见的,即使不是可取的。我觉得我有义务 - 作为一个 ThreadLocal 用户,为我的类提供一种方法来释放我的类附加到各种线程的资源。 p> 但是该怎么办呢? 在这里做什么是正确的? 对我来说,servlet引擎的线程复用策略似乎与 ThreadLocal 背后的意图不一致。 但也许我应该提供一个工具,允许用户说出与这个类相关联的线程特定的状态,即使我不能让线程模具让GC做它的东西?我可以这样做吗?我的意思是,它不像我可以安排 ThreadLocal#remove()被调用每个线程,看到 ThreadLocal#initialValue()在过去的某个时间。还是有另一种方式? 或者我应该对我的用户说去为自己提供一个体面的类加载器和线程池实现。 EDIT#1 :澄清如何在不会意识到线程生命周期的vanailla实用程序类中使用 threadCal EDIT #2 :修复了 DateSensitiveThing 解决方案 Sigh,这是老消息 好吧,这个晚会晚了一点。 2007年10月,Josh Bloch( java.lang.ThreadLocal 与Doug Lea的合着者)写了: 线程池的使用需要极其小心。线程的使用线程池与线程局部变量的混合使用可能导致意外的对象保留,正如在许多地方已经指出的那样。 人们一直在抱怨ThreadLocal与线程池的交互不良。但是Josh认可了: 每个线程的性能实例Aaron的SimpleDateFormat例子(上面)是这个模式的一个例子。 一些课程 如果您将任何类型的对象放入任何对象池,您必须提供一种方法来删除 如果您使用 ThreadLocal 来池化,那么这样做的选项有限。或者: a)您知道 Thread (s)您将在应用程序完成时终止; OR b)之后,您可以稍后安排调用ThreadLocal#set()的同一个线程,在您的应用程序终止时调用ThreadLocal#remove() 例如,你使用ThreadLocal作为对象池将对你的应用程序和你的类的设计造成沉重的代价。 因此,使用ThreadLocal可能是一个未成熟的优化,即使Joshua Bloch敦促您在有效的Java中考虑它。 简而言之,决定使用ThreadLocal作为对每个线程实例池的快速,无争议访问形式不是一个决定 注意:除了对象池,还有ThreadLocal的其他用法,这些课程不适用于那些只设置ThreadLocal的场景 库实现者的后果 b 您使用ThreadLocal,完全意识到您可能会使用额外的行李污染长时间运行的线程。如果你正在实现 java.util.concurrent.ThreadLocalRandom ,它可能是合适的。 (如果您不在 java。* 中实现,Tomcat可能仍然对您的库用户感兴趣。有趣的是注意到 java。使用ThreadLocal技术的原则。 或 您使用ThreadLocal并为您的类/包的客户提供:a )机会选择放弃优化(不使用ThreadLocal ...我不能安排清理);和b)一种方式来清理ThreadLocal资源(可以使用ThreadLocal ...我可以安排所有线程使用你调用 LibClass.releaseThreadLocalsForThread() $ b $ b 或 您可以为客户提供自己的对象池impelementation(可能使用ThreadLocal,或某种类型的同步)(OK,我可以给你一个 new ExpensiveObjectFactory< T>(){public T get(){...}} 如果你认为它是真正的neccesasry。 没有那么糟糕如果对象真的那么重要, 或 你决定它不值得你的应用程序,并找到一个不同的方法来处理这个问题。那些昂贵的创建,可变,非线程安全的对象会导致你痛苦...正在使用他们真的 不会共享对象 - 只需在本地作用域中实例化它们,并稍后丢弃。 不共享线程(除非你可以安排清理代码,当你喜欢) - 不要使用你的东西在JaveEE容器 线程池,足够聪明,清理ThreadLocals不打扰你。 / li> 在每个应用程序的基础上分配Threads,然后让它们在应用程序停止时死机的线程池。 池容器和应用程序,它们允许注册应用程序关闭处理程序,容器可以安排在已经用于为应用程序服务的线程上运行...在将来的某个时间,当该线程下一次可用时。例如。 servletContext.addThreadCleanupHandler(new Handler(){@ Override cleanup(){...}}) 在未来的JavaEE规范中,最后3个项目会出现一些标准化。 Bootnote 实际上, GregorianCalendar 的实例化非常轻量。这是不可避免的调用 setTime(),这会导致大部分工作。它也不保持线程的运行的不同点之间的任何显着状态。将日历添加到 ThreadLocal 中不太可能给你回来的费用,除非分析显示 new GregorianCalendar()中的热点。 new SimpleDateFormat(String) code>比较昂贵,因为它必须解析格式字符串。一旦被解析,对象的状态对于以后由相同线程使用是重要的。这是一个更好的适合。但实例化一个新的可能仍然是更便宜,而不是给你的类额外的责任。 My use of ThreadLocalIn my Java classes, I sometimes make use of a ThreadLocal mainly as a means of avoiding unnecessary object creation:@net.jcip.annotations.ThreadSafepublic class DateSensitiveThing { private final Date then; public DateSensitiveThing(Date then) { this.then = then; } private static final ThreadLocal<Calendar> threadCal = new ThreadLocal<Calendar>() { @Override protected Calendar initialValue() { return new GregorianCalendar(); } }; public Date doCalc(int n) { Calendar c = threadCal.get(); c.setTime(this.then): // use n to mutate c return c.getTime(); }}I do this for the proper reason - GregorianCalendar is one of those gloriously stateful, mutable, non-threadsafe objects, which provides a service across multiple calls, rather than representing a value. Further, it is considered to be 'expensive' to instantiate (whether this is true or not is not the point of this question). (Overall, I really admire it :-))How Tomcat WhingesHowever, if I use such a class in any environment which pools threads - and where my application is not in control of the lifecycle of those threads - then there is the potential for memory leaks. A Servlet environment is an good example.In fact, Tomcat 7 whinges like so when a webapp is stopped: SEVERE: The web application [] created a ThreadLocal with key of type [org.apache.xmlbeans.impl.store.CharUtil$1] (value [org.apache.xmlbeans.impl.store.CharUtil$1@2aace7a7]) and a value of type [java.lang.ref.SoftReference] (value [java.lang.ref.SoftReference@3d9c9ad4]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak. Dec 13, 2012 12:54:30 PM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks(Not even my code doing it, in that particular case).Who is to blame?This hardly seems fair. Tomcat is blaming me (or the user of my class) for doing the right thing. Ultimately, that's because Tomcat wants to reuse threads it offered to me, for other web apps. (Ugh - I feel dirty.) Probably, it's not a great policy on Tomcat's part - because threads actually do have/cause state - don't share 'em between applications.However, this policy is at least common, even if it is not desirable. I feel that I'm obliged - as a ThreadLocal user, to provide a way for my class to 'release' the resources which my class has attached to various threads.But what to do about it?What is the right thing to do here?To me, it seems like the servlet engine's thread-reuse policy is at odds with the intent behind ThreadLocal. But maybe I should provide a facility to allow users to say "begone, evil thread-specific state associated with this class, even though I am in no position to let the thread die and let GC do its thing?". Is it even possible for me to do this? I mean, it's not like I can arrange for ThreadLocal#remove() to be called on each of the Threads which saw ThreadLocal#initialValue() at some time in the past. Or is there another way? Or should I just say to my users "go and get yourself a decent classloader and thread pool implementation"?EDIT#1: Clarified how threadCal is used in a vanailla utility class which is unaware of thread lifecyclesEDIT#2: Fixed a thread safety issue in DateSensitiveThing 解决方案 Sigh, this is old newsWell, a bit late to the party on this one. In October 2007, Josh Bloch (co-author of java.lang.ThreadLocal along with Doug Lea) wrote: "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."People were complaining about the bad interaction of ThreadLocal with thread pools even then. But Josh did sanction:"Per-thread instances for performance. Aaron's SimpleDateFormat example (above) is one example of this pattern."Some LessonsIf you put any kind of objects into any object pool, you must provide a way to remove them 'later'.If you 'pool' using a ThreadLocal, you have limited options for doing that. Either:a) you know that the Thread(s) where you put values will terminate when your application is finished; ORb) you can later arrange for same thread that invoked ThreadLocal#set() to invoke ThreadLocal#remove() whenever your application terminatesAs such, your use of ThreadLocal as an object pool is going to exact a heavy price on the design of your application and your class. The benefits don't come for free.As such, use of ThreadLocal is probably a premature optimization, even though Joshua Bloch urged you to consider it in 'Effective Java'.In short, deciding to use a ThreadLocal as a form of fast, uncontended access to "per thread instance pools" is not a decision to be taken lightly.NOTE: There are other uses of ThreadLocal other than 'object pools', and these lessons do not apply to those scenarios where the ThreadLocal is only intended to be set on a temporary basis anyway, or where there is genuine per-thread state to keep track of.Consequences for Library implementorsThrere are some consequences for library implementors (even where such libraries are simple utility classes in your project).Either:You use ThreadLocal, fully aware that you might 'pollute' long-running threads with extra baggage. If you are implementing java.util.concurrent.ThreadLocalRandom, it might be appropriate. (Tomcat might still whinge at users of your library, if you aren't implementing in java.*). It's interesting to note the discipline with which java.* makes sparing use of the ThreadLocal technique.ORYou use ThreadLocal, and give clients of your class/package:a) the opportunity to choose to forego that optimization ("don't use ThreadLocal ... I can't arrange for cleanup"); ANDb) a way to clean up ThreadLocal resources ("it's OK to use ThreadLocal ... I can arrange for all Threads which used you to invoke LibClass.releaseThreadLocalsForThread() when I am finished with them.Makes your library 'hard to use properly', though.ORYou give your clients the opportunity to supply their own object-pool impelementation (which might use ThreadLocal, or synchronization of some sort). ("OK, I can give you a new ExpensiveObjectFactory<T>() { public T get() {...} } if you think it is really neccesasry".Not so bad. If the object are really that important and that expensive to create, explicit pooling is probably worthwhile.ORYou decide it's not worth that much to your app anyway, and find a different way to approach the problem. Those expensive-to-create, mutable, non-threadsafe objects are causing you pain ... is using them really the best option anyway?AlternativesRegular object pooling, with all its contended synchronization.Not pooling objects - just instantiate them in a local scope and discard later.Not pooling threads (unless you can schedule cleanup code when you like) - don't use your stuff in a JaveEE containerThread pools which are smart enough to clean-up ThreadLocals without whinging at you.Thread pools which allocate Threads on a 'per application' basis, and then let them die when the application is stopped.A protocol between thread-pooling containers and applications which allowed registration of a 'application shutdown handler', which the container could schedule to run on Threads which had been used to service the application ... at some point in the future when that Thread was next available. Eg. servletContext.addThreadCleanupHandler(new Handler() {@Override cleanup() {...}})It'd be nice to see some standardisation around the last 3 items, in future JavaEE specs.BootnoteActually, instantiation of a GregorianCalendar is pretty lightweight. It's the unavoidable call to setTime() which incurs most of the work. It also doesn't hold any significant state between different points of a thread's exeuction. Putting a Calendar into a ThreadLocal is unlikely to give you back more than it costs you ... unless profiling definitely shows a hot spot in new GregorianCalendar().new SimpleDateFormat(String) is expensive by comparison, because it has to parse the format string. Once parsed, the 'state' of the object is significant to later uses by the same thread. It's a better fit. But it might still be 'less expensive' to instantiate a new one, than give your classes extra responsibilities. 这篇关于当类暴露给线程池时,它是否真的是我的工作清理ThreadLocal资源?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 10-29 16:26