12

我在日志记录类中定义了一个静态对象,大致如下:

   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 中使用的对象不在同一范围级别...但这只是一个猜测。

谢谢你的帮助。

4

4 回答 4

19

静态不是“类的所有实例共享的”——它们与实例无关;它们属于类型本身。特别是,静态变量完全可用,无需创建任何实例。

这提供了关于静态范围的线索:它们由Class表示包含类的对象限定,而包含类又由ClassLoader加载它的对象限定。

根据库的放置位置,静态变量可能是 JVM 范围或 Web 应用程序范围 - 或者可能介于两者之间,如果 Tomcat 支持多个托管(我不记得临时)。

查看 Tomcat 文档,了解这些库的布局方式以及它们与类加载器的关系。例如,这里是Tomcat 6.0 ClassLoader how-to guide5.5 的等价物

你的布尔“锁”是如何工作的?您确实应该使用适当的锁 ( synchronized) 来确保属性对象的每次使用(读取和写入,包括在您遍历它的整个期间的锁定)都被适当地锁定。

您是否考虑过将其视为不可变的,而不是更改“实时”Properties对象 - 所以当您想要更新属性时,您需要复制,更改它,然后将副本设为“实时”版本?您仍然需要防止两个不同的线程同时进行更改(或者您会丢失一些),但这可能会使阅读方面更容易和更高效。

于 2009-09-09T20:59:12.410 回答
5

您可能会发现此类static变量的范围仅限于每个已加载您的类的 ClassLoader。我不确定 Tomcat 是如何安排它的 ClassLoaders 的,所以很难说在那个环境中范围的范围是什么。

于 2009-09-09T20:58:01.540 回答
3

您的可能原因是您在一个线程ConcurrentModificationException中迭代Properties对象的值/条目,而另一个线程同时修改它。你不可以做这个。

您能否详细说明您在此处提到的锁定机制:

当我的类更新它时,我使用静态布尔变量来“锁定”属性对象,但这并不能阻止异常的发生。

?

因为这听起来不像是在使用 Java 中的内置锁定和同步方法。

这样的事情应该防止线程在另一个线程更新它时读取 Properties 对象:

static Object lockObject = new Object();

...

synchronized(lockObject) {
     // access the Properties object
}

请注意,每次访问 Properties 对象时都需要执行此操作,以读取或修改它。

此外,我永远不会推荐静态对象在所有实例或静态 lockObjects 之间共享数据——全局数据是邪恶的——但听起来你好像出于某种原因需要这个。

于 2009-09-09T21:07:23.473 回答
0

这可能是一个类加载器问题,其中包含您的类的 jar 在您不同应用程序的每个 WEB-INF/lib 中重复?如果是这样,我会尝试将此 jar 添加到 Tomcat 库而不是应用程序中。

于 2009-09-09T21:06:54.437 回答