56

在 Java 中,我们使用 System.setProperty() 方法来设置一些系统属性。根据这篇文章,系统属性的使用有点棘手。

System.setProperty() 可能是一个邪恶的调用。

  • 它是 100% 线程敌对的
  • 它包含超全局变量
  • 当这些变量在运行时发生神秘变化时,调试起来非常困难。

我的问题如下。

  1. 系统属性的范围如何?它们是特定于每个虚拟机的,还是具有“超级全局性质”,在每个虚拟机实例上共享相同的属性集?我猜选项1

  2. 是否有任何工具可用于监视运行时更改以检测系统属性的更改。(只是为了便于问题检测)

4

7 回答 7

51

系统属性的范围

至少通过阅读该System.setProperties方法的 API 规范,我无法得到系统属性是否由 JVM 的所有实例共享的答案。

为了找出答案,我编写了两个快速程序,它们将通过 设置系统属性System.setProperty,使用相同的键,但值不同:

class T1 {
  public static void main(String[] s) {
    System.setProperty("dummy.property", "42");

    // Keep printing value of "dummy.property" forever.
    while (true) {
      System.out.println(System.getProperty("dummy.property"));
      try {
        Thread.sleep(500);
      } catch (Exception e) {}
    }
  }
}

class T2 {
  public static void main(String[] s) {
    System.setProperty("dummy.property", "52");

    // Keep printing value of "dummy.property" forever.
    while (true) {
      System.out.println(System.getProperty("dummy.property"));
      try {
        Thread.sleep(500);
      } catch (Exception e) {}
    }
  }
}

(请注意,运行上面的两个程序会使它们进入无限循环!)

事实证明,当使用两个单独java的进程运行这两个程序时,一个 JVM 进程中设置的属性值不会影响另一个 JVM 进程的值。

我应该补充一点,这是使用 Sun 的 JRE 1.6.0_12 的结果,并且至少在 API 规范中没有定义这种行为(或者我无法找到它),行为可能会有所不同。

是否有任何工具可以监控运行时更改

据我所知不是。但是,如果确实需要检查系统属性是否有更改,则可以一次保留 的副本Properties,并将其与另一个调用进行比较System.getProperties-- 毕竟,Properties它是 的子类Hashtable,因此比较将是以类似的方式进行。

下面的程序演示了一种检查系统属性是否发生变化的方法。可能不是一个优雅的方法,但它似乎完成了它的工作:

import java.util.*;

class CheckChanges {

  private static boolean isDifferent(Properties p1, Properties p2) {
    Set<Map.Entry<Object, Object>> p1EntrySet = p1.entrySet();
    Set<Map.Entry<Object, Object>> p2EntrySet = p2.entrySet();

    // Check that the key/value pairs are the same in the entry sets
    // obtained from the two Properties.
    // If there is an difference, return true.
    for (Map.Entry<Object, Object> e : p1EntrySet) {
      if (!p2EntrySet.contains(e))
        return true;
    }
    for (Map.Entry<Object, Object> e : p2EntrySet) {
      if (!p1EntrySet.contains(e))
        return true;
    }

    return false;
  }

  public static void main(String[] s)
  {
    // System properties prior to modification.
    Properties p = (Properties)System.getProperties().clone();
    // Modification of system properties.
    System.setProperty("dummy.property", "42");
    // See if there was modification. The output is "false"
    System.out.println(isDifferent(p, System.getProperties()));
  }
}

属性不是线程安全的?

Hashtable 是 thread-safe,所以我期待它Properties也是如此,事实上,Properties该类的 API Specifications 证实了这一点:

这个类是线程安全的:多个线程可以共享一个Properties 对象而不需要外部同步。,序列化形式

于 2009-05-26T04:44:38.487 回答
13

系统属性是每个进程的。这意味着它们比静态字段更具全局性,静态字段是每个类加载器。因此,例如,如果您有一个运行多个 Java webapps 的 Tomcat 实例,每个实例都有一个com.example.Example带有名为 的静态字段的类globalField,那么这些 webapps 将共享系统属性,但com.example.Example.globalField可以在每个 webapp 中设置为不同的值。

于 2013-08-30T00:04:21.353 回答
5

每个 VM 有一份属性副本。它们与其他静态(包括单例)有很多相同的问题。

我想,作为一个黑客,你调用System.setProperties一个版本的Properties响应会根据上下文(线程、调用堆栈、一天中的时间等)而有所不同。它还可以记录任何更改System.setProperty。您还可以为SecurityManager相关权限设置日志安全检查。

于 2009-05-26T04:39:53.593 回答
4

是的,“系统属性”是每个虚拟机的(尽管有许多“神奇”属性包含有关主机系统的信息,例如:“os.name”、“os.arch”等)。

至于您的第二个问题:我不知道这样的工具,但如果您担心系统属性会发生变化,您可以使用特殊的SecurityManager来防止(甚至可能跟踪)系统属性更改。

于 2009-05-26T04:38:56.013 回答
3

它们的范围是正在运行的 JVM,但除非您有一些深奥的类加载器问题,否则带有属性对象的静态变量将执行相同的操作,并且有机会同步或执行您需要的任何其他操作。

于 2009-05-26T04:41:28.337 回答
0

当您启动一个新的 JVM 时,它会复制环境变量并将它们用于它的所有生命周期。因此,如果您对该环境进行更改,它们将仍然局限于此。我遇到并正在调查的奇怪行为略有不同:如果我启动一个 JVM,它声明了一些影响我在应用程序中使用的库的行为的环境变量(cmd 行上的 -D 参数)。但是,如果在 java 代码中(它们是可见的),我对它们进行了更改,那么这些更改似乎不会影响库的行为。奇怪的是我们在同一个JVM中!

于 2017-10-26T07:23:55.053 回答
-1

你没有说你使用系统属性的动机是什么。

我们使用 Spring 进行配置,并使用注入 XML 的属性文件设置初始属性。应用程序运行时对配置的更改是使用 JMX 进行的。

当然,还有许多其他方法可以使用属性文件、基于 xml 的配置等来更改 Java 中的配置。

于 2009-05-26T05:50:39.093 回答