Java系统属性的范围

56

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

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

  • 它是100%线程敌对的
  • 它包含超级全局变量
  • 当这些变量在运行时神秘地改变时,它极其难以调试。

我的问题如下。

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

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

7个回答

53

系统属性的作用域

至少从阅读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的另一个调用进行比较--毕竟,PropertiesHashtable的子类,因此比较方式类似。

以下是一个演示如何检查系统属性是否更改的程序。这可能不是一种优雅的方法,但它似乎可以完成工作:

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()));
  }
}

Properties类不是线程安全的吗?

我原本以为Hashtable是线程安全的,因此我也期望 Properties 是线程安全的,而且事实上,Properties 类的API规范也确认了这一点:

这个类是线程安全的: 多个线程可以共享一个单独的Properties对象, 而无需外部同步。, Serialized Form


13

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


1
@Dejel 它们不是线程本地的。同一JVM进程中的所有线程都有一组共享的系统属性值。 - Max Nanasy

4
是的,“系统属性”是每个虚拟机独立的(尽管有些“神奇”的属性包含有关主机系统的信息,例如:“os.name”,“os.arch”等)。
至于您的第二个问题:我不知道这样的工具,但如果您担心系统属性被更改,可以使用特殊的SecurityManager来防止(甚至跟踪)系统属性的更改。

4
每个虚拟机只有一个属性副本。它们与其他静态变量(包括单例)一样存在许多问题。
我猜,可以通过调用根据上下文(线程、调用堆栈、时间等)不同而有不同响应的Properties版本的System.setProperties来进行黑客攻击。还可以使用System.setProperty记录任何更改。您还可以设置一个SecurityManager来记录相关权限的安全检查。

3

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


0

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


我的问题已经通过阅读此链接解决:https://dev59.com/TXfZa4cB1Zd3GeqPX_T9#19362517 - Robin73

-1

您没有说明使用系统属性的动机。

我们使用Spring进行配置,并使用注入到XML中的属性文件设置初始属性。在应用程序运行时,通过使用JMX来进行配置更改。

当然,还有许多其他方法可以使用Java进行配置更改,例如使用属性文件、基于XML的配置等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接