Java 在循环setproperty之前,是否应该先获取属性锁?

Java 在循环setproperty之前,是否应该先获取属性锁?,java,performance,properties,thread-safety,synchronized,Java,Performance,Properties,Thread Safety,Synchronized,根据java类的文档,java类是线程安全类: 这个类是线程安全的:多个线程可以共享一个属性对象,而不需要外部同步 由于这个原因,我习惯于在Map的HashMap实现中维护我的属性,这不是线程安全的,而是更轻量级的。更具体地说,实现线程安全性需要额外的锁定机制,这将对性能产生影响。我可以通过简单地在静态类初始值设定项中隔离属性初始化来避免这种情况,这将保证在使用同一类的实例方法中的任何get调用之前完成初始化 到目前为止,这只是一个故事,引导我进入实际问题。最后,我需要恢复到只能接受“属性”作为

根据java类的文档,java类是线程安全类:

这个类是线程安全的:多个线程可以共享一个属性对象,而不需要外部同步

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

到目前为止,这只是一个故事,引导我进入实际问题。最后,我需要恢复到只能接受“属性”作为参数的API,例如。我需要将我的
映射
转换为
属性

因此,第一次尝试看起来像这样:

  Properties properties = new Properties();
  this.propertyMap.forEach((k,v)->{properties.setProperty(k, v);});
  connection = DriverManager.getConnection(url, properties);
明显的问题,或者可能不像我避免使用实际的for循环那样明显,是我重复调用了
Properties.setProperty
。如果
Properties
确实是线程安全的,那么这就意味着对
setProperty
的每个调用都是同步的,并且添加了单独的锁定/解锁机制

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


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

我基于不正确的基准测试得出了无效的结论。尽管我尝试了一些热身活动,但这还远远不够

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

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

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

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

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

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

+----------------------------------+----------+--------+ | 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 | +----------------------------------+----------+--------+
完整的源代码可在GitHub上获得:。

属性的本地实例上同步没有意义。我会考虑锁定<代码> PrimyPAP,但是每次你想更新地图时,你也需要锁定它,以防止线程锁之间可能发生的变异。此外,无争用同步也很快。所以这可能不值得为之烦恼。@JoD。因为您创建了它的本地实例。如果两个线程正在运行您的代码,那么这两个线程都将有一个
Properties
对象的不同实例,因此这两个线程都将同时在
synchronized
块中执行代码,从我的角度看,这只是浪费时间/资源。我认为这是过早优化。您是否衡量过这种所谓的对性能的影响?没有使用
properties.putAll(propertyMap)的任何原因?它将只获得一次锁…
+----------------------------------+----------+--------+
|               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  |
+----------------------------------+----------+--------+
  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());