Iterator 在内部是可变的,所以如果你在多线程环境中使用它,你必须考虑到这一点。如果你保证你不会在以下情况下结束,例如
- 2个线程检查
hasNext()
- 其中一个调用
next() - 它恰好是最后一个元素
- 其他调用
next() - NPE
(或类似的)那么你应该没问题。在您的示例中,Iterator 甚至没有离开范围,因此错误不应该来自 Iterator。
但是,在您的代码中,我看到 aObject.wait() 和 aObject.notifyAll() 彼此相邻的问题 - 如果您调用 .wait,那么您将无法到达 .notifyAll,这将取消阻止它。您可以在 REPL 中检查它是否挂起:
@ val anObject = new Object { def foo() = throw new Exception }
anObject: {def foo(): Nothing} = ammonite.$sess.cmd21$$anon$1@126ae0ca
@ anObject.synchronized {
if (Try(anObject.foo()).isFailure) {
Iterator.continually {
anObject.wait()
Try(anObject.foo())
}.dropWhile(_.isFailure).next()
}
anObject.notifyAll()
}
// wait indefinitelly
我建议将设计更改为不依赖wait 和notifyAll。但是,从您的代码中很难说出您想要实现的目标,所以我无法判断这是否更像 Promise-Future 案例、monix.Observable、monix.Task 或其他。
如果您的用例是一个队列、生产者和消费者,那么它听起来像是反应流的用例 - 例如。 FS2 + Monix,但它可能是 FS2+IO 或来自 Akka Streams 的东西
val queue: Queue[Task, Item] // depending on use case queue might need to be bounded
// in one part of the application
queue.enqueu1(item) // Task[Unit]
// in other part of the application
queue
.dequeue
.evalMap { item =>
// ...
result: Task[Result]
}
.compile
.drain
这种方法需要对设计应用程序的想法进行一些改变,因为您将不再直接在线程上工作,而是设计一个流数据并声明什么是顺序的,什么可以并行完成,其中线程只是一个实现细节。