方案
我们正在开发一个API,可以在多线程环境中处理大约2-3百万次点击每小时。服务器是Apache Tomcat 7.0.64。
我们有一个包含大量数据的自定义对象,我们称之为XYZDataContext。当有新请求进入时,我们将XYZDataContext对象与请求上下文相关联。每个请求一个XYZDataContext对象。我们将并行生成各种线程,以便为从XYZDataContext对象中收集/处理数据的请求提供服务。我们将并行处理事物的线程需要访问这个XYZDataContext对象 避免在应用程序的各处传递此对象,以及各种对象/方法/线程, 我们正在考虑让它成为一个threadlocal。线程将使用来自XYZDataContext对象的数据,并且还将更新此对象中的数据。 当线程完成时,我们计划将生成的子线程中更新的XYZDataContext对象的数据合并到主线程的XYZDataContext对象中。
我的问题:
这是一个好方法吗?
线程池风险 - Tomcat服务器将维护一个线程池,我读到使用threadlocal与线程池是一个灾难,因为线程不是每个说的GC并且被重用,所以对threadlocal对象的引用将不会得到GCed并且将导致在我们不再需要的内存中存储大量对象,最终导致OutOfMemory问题......
除非它们被引用为弱引用,因此立即获得GC
我们正在使用Java 1.7开放JDK。我看到了ThreadLocal的源代码,尽管ThreadLocalMap.Entry
是一个弱引用,它与ReferenceQueue没有关联,并且Entry构造函数的注释说“,因为没有使用引用队列,所以过时的条目保证是仅在表开始空间不足时才删除。“
我想这在缓存的情况下效果很好但在我们的情况下不是最好的。我希望threadlocal XYZDataContext对象立即被GCed。 ThreadLocal.remove()
方法在这里有效吗?
有没有办法在下一次GC运行中强制清空空间?
这是使用ThreadLocal对象的正确方案吗?或者我们是否滥用threadlocal概念并在不应该使用它的地方使用它?
答案 0 :(得分:1)
我的直觉告诉我你走错了路。由于您已经有一个中央上下文对象(一个用于所有线程)并且您希望同时从多个线程访问它,我将使用一个托管上下文对象的Singleton并提供线程安全方法来访问它。
我强烈建议您同时进行所有操作,而不是操纵上下文对象的多个属性。如果只传递一个包含要在上下文对象中更改的所有属性的对象,则最好。
例如
Singleton.getInstance().adjustContext(ContextAdjuster contextAdjuster)
您可能还想考虑使用线程安全队列,用线程中的ContextAdjuster
对象填充它,最后在Context的线程中处理它。
Google用于Java中的并发,阻止和非阻塞队列。我相信你会找到大量的示例代码。