在同步块中修改对象(例如 HashSet)是否等同于使其成为同步对象?

Is modifying an object (e.g. a HashSet) in a synchronized block equivalent to making it a synchronized object?

我知道您可以使用

获得线程安全的 HashSet
HashSet<String> mySynchronizedHashSet = Collections.synchronizedSet(new HashSet<String>());

但是每次要修改它时,使用传统的 HashSet 并用 synchronized 块将其括起来怎么样?

synchronized(myRegularHashSet) {
    myRegularHashSet.add(new String());
}

除了每次要安全地修改集合时都要记住添加 synchronized 关键字的缺点之外,这两种技术是否等效?我需要同时使用两者吗?他们在性能方面如何比较?假设HashSet只有在已知且罕见的情况下才会有并发修改的风险,只有在必要时才同步,是否可以节省时间?

编辑 1:刚刚找到 ,它回答了我的一些问题(尽管不是关于性能的问题)。

编辑 2:为了解决我自己的问题之一“(我是否需要同时使用两者?”),我将引用 the Javadoc,“用户必须手动同步返回的通过 Iterator、Spliterator 或 Stream 遍历它时的集合。”我假设这意味着同步块在使用时总是必要的,例如,一个迭代器,但是没有必要使用同步块一个同步对象。

只要您仅从同步方法或块内部访问 HashSet(并且在同一个对象上同步!),它就是等效的。我倾向于更喜欢 'synchronized' 关键字而不是集合的特殊形式(但是,我驾驶变速杆)。

在大多数情况下,尝试在需要同步时变得聪明一点好处都没有,因为在没有并发的情况下进行同步通常会产生开销,而这些开销会在噪音中丢失。此外,同步还会影响更改的可见性。