题
我在我的日志记录类中定义了一个静态对象,类似于:
class myLoggingClass {
static java.util.Properties properties;
...
...
}
根据我的参考书,这意味着属性对象由我的所有实例共享。
我觉得这个定义不够。我正在编写一个在我们项目的每个应用程序中多次调用的类。
此外,我们的项目使用在同一个tomcat容器中运行的多个Web服务。每个Web服务可能有多个线程。
主机上运行的Java虚拟机还可以运行一个或多个Web服务客户端应用程序,这些应用程序在tomcat外部运行。
因此,通过这个定义,我可能让tomcat运行带有线程的多个Web服务,每个线程都有几个对象,这些对象可能包含我的类的实例。
可能还有一个或两个Web客户端在tomcat之外运行,但在同一个JVM中。我的类的这些实例的所有会共享相同的属性对象吗?这将使其成为JVM范围。
如果静态对象不 JVM范围内,是否有人知道每个对象存在的级别?每个tomcat容器一个?每个Web服务一个,每个独立的Web服务客户端应用程序一个?
原因:当我更新我的属性时,我从java.util.Properties获得了java.lang.ConcurrentUpdateException。
我正在使用静态布尔变量来“锁定”我的类更新它时的属性对象,但这不会阻止异常发生。
这让我相信我的类中使用的静态对象可能与java.util.Properties中使用的静态对象没有相同的范围级别......但这只是猜测。
感谢您的帮助。
解决方案
ConcurrentModificationException
的可能原因是您在一个线程中通过 Properties
对象的值/条目进行迭代,而另一个线程同时修改它。你不能这样做。
您能否详细说明您在此提及的锁定机制:
我正在使用静态布尔变量来“锁定”我的类更新它时的属性对象,但这不会阻止异常发生。
因为它听起来不像是在Java中使用内置的锁定和同步方法。
这样的事情会阻止线程读取Properties对象,而另一个线程更新它:
static Object lockObject = new Object();
...
synchronized(lockObject) {
// access the Properties object
}
请注意,您需要执行每次访问Properties对象,以便阅读或修改它。
此外,我永远不会建议静态对象在所有实例或静态lockObjects之间共享数据 - 全局数据是邪恶的 - 但听起来好像你出于某种原因需要它。
其他提示
静态不是“由类的所有实例共享”。 - 他们与实例无关;它们属于类型本身。特别是,如果没有创建任何实例,静态变量是完全可用的。
这给出了关于静态范围的线索:它们由表示包含类的 Class
对象限定,而该类又由 ClassLoader
确定范围。加载它。
根据库的放置位置,静态变量可能是JVM范围的,也可能是Web应用程序范围的 - 或者可能是介于两者之间的东西,如果Tomcat支持多个托管(我不记得了)。
查看Tomcat文档,了解库的布局方式以及它们与类加载器的关系。例如,这里是 Tomcat 6.0 ClassLoader使用指南和相当于5.5 。
你的布尔“锁定”是怎样的?工作?您应该使用正确的锁( synchronized
)来确保每次使用属性对象(包括读取和写入,包括在整个迭代期间锁定)都被适当地锁定。
而不是改变“直播” Properties
对象,您是否考虑过将其视为不可变 - 所以当您想要更新属性时,您需要复制,更改它,然后将副本复制为“实时”。版?你仍然需要阻止两个不同的线程同时进行更改(或者你会丢失一些),但它可能会使阅读方面更容易和更有效。
您可能会发现这样的 static
变量的范围仅限于已加载您的类的ClassLoader。我不确定Tomcat如何安排其ClassLoader,因此很难说该范围在该环境中的范围。
这可能是一个类加载器问题,其中包含您的类的jar在您的不同应用程序的每个WEB-INF / lib中都是重复的吗? 如果是这样,我会尝试将此jar添加到Tomcat库而不是应用程序。