- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我们知道,默认情况下迭代并发集合不是线程安全的,所以不能使用:
Set<E> set = Collections.synchronizedSet(new HashSet<>());
//fill with data
for (E e : set) {
process(e);
}
这是因为在迭代过程中可能会添加数据,因为 set
上没有排他锁。
这在 javadoc 中有描述Collections.synchronizedSet
:
public static Set synchronizedSet(Set s)
Returns a synchronized (thread-safe) set backed by the specified set. In order to guarantee serial access, it is critical that all access to the backing set is accomplished through the returned set.
It is imperative that the user manually synchronize on the returned set when iterating over it:
Set s = Collections.synchronizedSet(new HashSet());
...
synchronized (s) {
Iterator i = s.iterator(); // Must be in the synchronized block
while (i.hasNext())
foo(i.next());
}Failure to follow this advice may result in non-deterministic behavior.
然而,这不适用于Set.forEach
,它从Iterable.forEach 继承了默认方法forEach
.
现在我查看了源代码,在这里我们可以看到我们有以下结构:
Collections.synchronizedSet()
。我们得到一个:
public static <T> Set<T> synchronizedSet(Set<T> s) {
return new SynchronizedSet<>(s);
}
...
static class SynchronizedSet<E>
extends SynchronizedCollection<E>
implements Set<E> {
private static final long serialVersionUID = 487447009682186044L;
SynchronizedSet(Set<E> s) {
super(s);
}
SynchronizedSet(Set<E> s, Object mutex) {
super(s, mutex);
}
public boolean equals(Object o) {
if (this == o)
return true;
synchronized (mutex) {return c.equals(o);}
}
public int hashCode() {
synchronized (mutex) {return c.hashCode();}
}
}
它扩展了 SynchronizedCollection
,除了显而易见的方法之外,还有以下 有趣的 方法:
// Override default methods in Collection
@Override
public void forEach(Consumer<? super E> consumer) {
synchronized (mutex) {c.forEach(consumer);}
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
synchronized (mutex) {return c.removeIf(filter);}
}
@Override
public Spliterator<E> spliterator() {
return c.spliterator(); // Must be manually synched by user!
}
@Override
public Stream<E> stream() {
return c.stream(); // Must be manually synched by user!
}
@Override
public Stream<E> parallelStream() {
return c.parallelStream(); // Must be manually synched by user!
}
这里使用的 mutex
与 Collections.synchronizedSet
的 all 操作锁定到的对象相同。
现在我们可以,根据实现判断,使用 Collections.synchronizedSet(...).forEach(...)
是线程安全的,但是它也是线程安全的规范吗?
(令人困惑的是,Collections.synchronizedSet(...).stream().forEach(...)
在实现上是 not 线程安全的,并且结论规范似乎也是未知的。)
最佳答案
正如你所写,从实现来看,forEach()
对于 JDK 提供的集合(参见下面的免责声明)是线程安全的,因为它需要获取互斥锁的监视器才能继续。
Is it also thread safe by specification?
我的意见 - 不,这里有一个解释。 Collections.synchronizedXXX()
用简短的语言重写的 javadoc 说 - “所有方法都是线程安全的,除了那些用于迭代它的方法”。
我的另一个,虽然非常主观的论点是 yshavit 写的 - 除非被告知/阅读,否则请考虑 API/类/任何不是线程安全的。
现在,让我们仔细看看 javadocs。我想我可以说明该方法forEach()
用于对其进行迭代,因此,按照 javadoc 的建议,我们应该认为它不是线程安全的,尽管它与现实相反(实现)。
无论如何,我同意 yshavit 的声明,即应该更新文档,因为这很可能是文档,而不是实现缺陷。但是,除了 JDK 开发人员之外,没有人可以肯定地说,请参阅下面的问题。
我想在本次讨论中提到的最后一点——我们可以假设自定义集合可以用 Collections.synchronizedXXX()
包装。 ,以及 forEach()
的实现这个集合可以是……可以是任何东西。该集合可能对 forEach()
中的元素执行异步处理。方法,为每个元素生成一个线程......它仅受作者想象的限制,同步(互斥)包装不能保证这种情况下的线程安全。该特定问题可能是不声明 forEach()
的原因。作为线程安全的方法..
关于java - 通过 Collections.synchronizedSet(...).forEach() 进行的迭代是否保证是线程安全的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23450227/
是否有带有索引的.collect?我想做这样的事情: def myList = [ [position: 0, name: 'Bob'], [position: 0, name: 'J
我创建了一个 Collection 类,它扩展了 ArrayList 以添加一些有用的方法。它看起来像这样: public class Collection extends ArrayList {
我知道如果我有元素,我想得到 List/Set/Map 我可以调用这个元素: Collections.singleton()/Collections.singletonList()/Collectio
我刚刚在我的 pom 文件中看到 Apache commons-collections 有两个不同的组 ID: commons-collections commons-collect
我们可以对所有 Collections 类型的对象(如 Set 和 List)使用 Collections.synchronizedCollection(Collection c),这就是为什么我们有
我有List>我想让它把上一个集合中的所有人复制到List收藏。 我是这样做的: var People = new List>{ new List{...},... };
我想做的是使用良好的旧循环非常简单。 假设我有一个包含 B 列表的对象 A。 public class A { public List myListOfB; } 在其他一些方法中,我有一个 As
在 Capgemini 的采访中,我被问到一个我无法回答的问题。所有集合类和接口(interface)共有的那些方法是什么? 最佳答案 所有 java 对象类(包括所有集合)都派生自名为 Object
我有一系列存储估计信息的数据库表。当设置某些边界时,我试图从所有数据库表中返回所有数据。 收藏 $estimateItems = new Collection(); $esti
为什么 Haskell 实现如此专注于链表? 例如,我知道 Data.Sequence 效率更高 大多数列表操作(cons 操作除外),并且被大量使用; 但是,从语法上讲,它“几乎不受支持”。 Has
我试图简单地将我在 PHP 中请求的内容返回到 JSON。我的问题是每个库存尚未完成。事实上,它是“渲染”,但“this.collection.models”尚未完成,因为请求尚未完成。 我应该如何解
本质上,作为Powershell脚本的一部分,我需要实现广度优先搜索。因此,我需要队列,并且认为System.Collections.Queue与其他任何队列一样好。但是,当我从队列中取出一个对象时,
已关闭。这个问题是 off-topic 。目前不接受答案。 想要改进这个问题吗? Update the question所以它是 on-topic用于堆栈溢出。 已关闭10 年前。 Improve t
嗨,我不明白为什么这不起作用? Notifications.update({'userId':Meteor.userId(), 'notifyUserId':notifyFriendId}, {$se
假设我有一个闭包: def increment = {value, step -> value + step } 现在我想遍历我的整数集合的每个项目,用 5 递增,并将新元素保存到一个新集合中:
使用逐页 View 时,我的 plone 集合文件夹未显示所有项目。基本上我有 9 页包含元素,但第 6 - 8 页显示的内容完全相同。因此,并非所有项目都会显示,即使项目总数对应于应该在集合中的元素
private Map> map ,其中 ProgramCourse 是我的项目中的域类,上面的 map 是我运行项目时域类 Program 的字段以下异常即将到来。 Use of @OneToMan
三者的主要区别是什么?现在,我想分别使用字符串/字符串创建一个键/值对。这三个似乎都有我可以使用的选项。 编辑:我只想创建一个简单的哈希表 - 没什么特别复杂的。 最佳答案 通用集合几乎完全取代了基础
我正在为 NodeJs 使用 mongodb 驱动程序,其中有 3 个方法: 1) db.collection.insert 2) 数据库.collection.insertOne 3) db.col
我有一个集合,我正在尝试使用 Distinct 方法删除重复项。 public static Collection imagePlaylist imagePlaylist = imagePlaylis
我是一名优秀的程序员,十分优秀!