有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java在循环setProperty之前,我应该先获得属性锁吗?

java类^{}是一个线程安全类,根据其文档:

This class is thread-safe: multiple threads can share a single Properties object without the need for external synchronization.

由于这个原因,我习惯在MapHashMap实现中维护我的属性,这不是线程安全的,而是更轻量级的。更具体地说,实现线程安全性需要额外的锁定机制,这将对性能产生影响。我可以简单地通过在静态类初始值设定项中隔离属性初始化来避免这种情况,这将保证在使用同一类的实例方法中的任何get调用之前完成初始化

到目前为止,这只是一个故事,引导我进入真正的问题。最后,我需要回到只能接受“Properties”作为参数的API,例如^{}。我需要把我的Map转换成Properties

所以第一次尝试看起来像这样:

  Properties properties = new Properties();
  this.propertyMap.forEach((k,v)->{properties.setProperty(k, v);});
  connection = DriverManager.getConnection(url, properties);

明显的问题,或者可能不像我避免使用实际的for循环那样明显,是我使用了对Properties.setProperty的重复调用。如果Properties确实是线程安全的,那么这就意味着对setProperty的每个调用都是同步的,并且添加了单独的锁定/解锁机制

在这种情况下,我首先手动锁定整个Properties实例(如下面的代码所示)不是更好吗

Connection connection;
Properties properties = new Properties();
synchronized (properties) {
  // Properties is implemented as thread-safe. As it adds
  // additional thread locking, it's use is localized to just
  // here to avoid consequential performance issues. We will
  // do a last minute conversion from Map to Properties right here.
  this.propertyMap.forEach((k,v)->{properties.setProperty(k, v);});
  connection = DriverManager.getConnection(url, properties);
}

我可能预料到的一个问题是,手动锁定属性和对setProperties的单独调用可能会导致死锁,但它似乎运行正常


共 (1) 个答案

  1. # 1 楼答案

    我根据不恰当的基准得出了无效的结论。尽管我尝试过某种形式的热身运动,但这还远远不够

    只有在使用完整数据集的第三次迭代(添加迭代后)时,测量值才趋于平稳,并且在彼此的范围内,因此它们可以被称为相同,但差异不大

    修正结论

    现在的结论是,这些方法实际上几乎不相关。我仍然可以看到HashMap总体上比Properties好一些,但不足以让我选择性能而不是可读性

    根据@Holger的建议,我建议只使用Properties.putAll(other)方法。它速度很快,性能和其他的差不多

    但是,如果您在多线程环境中使用这个,我建议使用^ {CD1>},并仔细考虑并发访问。通常,这很容易实现,无需过度锁定/同步

    我认为这是一个很好的教训

    下面是更标准化的测量结果。我通过目测和挑选一个没有异常值的样本,而不是尝试对多个样本进行平均/中位数,这有点作弊。只是为了避免这样做的复杂性(和额外的工作),或者必须获得一个外部工具

    +                 +     +    +
    |               Name               | Unlocked | Locked |
    +                 +     +    +
    | putAll->Properties               | 1,644    | 1,429  |
    | forEach->setProperty->Properties | 1,474    | 1,740  |
    | stream->setProperty->Properties  | 1,484    | 1,735  |
    | loop->setProperty->Properties    | 2,022    | 1,606  |
    | forEach->put->Properties         | 1,590    | 1,411  |
    | stream->put->Properties          | 1,538    | 1,514  |
    | loop->put->Properties            | 1,380    | 1,666  |
    | putAll->Map                      | 1,380    | 509    |
    | forEach->put->Map                | 935      | 915    |
    | stream->put->Map                 | 927      | 888    |
    | loop->put->Map                   | 880      | 1,015  |
    +                 +     +    +
    

    方法论

    测试方法的顺序与上表相同,以pPropertiesma Map为目标:

      p.putAll(SOURCEMAP);
      SOURCEMAP.forEach(p::setProperty);
      SOURCEMAP.entrySet().stream().forEach((e)->{p.setProperty(e.getKey(), e.getValue());});
      for (Map.Entry<String,String> e:SOURCEMAP.entrySet()) p.setProperty(e.getKey(), e.getValue());
      SOURCEMAP.forEach(p::put);
      SOURCEMAP.entrySet().stream().forEach((e)->{p.put(e.getKey(), e.getValue());});
      for (Map.Entry<String,String> e:SOURCEMAP.entrySet()) p.put(e.getKey(), e.getValue());
      m.putAll(SOURCEMAP);
      SOURCEMAP.forEach(m::put);
      SOURCEMAP.entrySet().stream().forEach((e)->{m.put(e.getKey(), e.getValue());});
      for (Map.Entry<String,String> e:SOURCEMAP.entrySet()) m.put(e.getKey(), e.getValue());
    

    完整的源代码可以在GitHub上获得:http://github.com/jdesmet/properties-benchmarking