5

What is the correct way to create a thread-safe, infinite circular iterator in scala? It seems that the following is not thread safe (iterating simulataneously from multiple threads on iterator occasionally throws exceptions):

val map = Map(1->"one", 2->"two")
val iterator = Iterator.continually(map).flatten

How would you correct this to make it thread-safe?

joniba
  • 3,339
  • 4
  • 35
  • 49
  • 1
    An iterator cannot really be made thread safe, because (1) it is inheratenly mutable and (2) using an operator involves calling two distinct methods: `hasNext`and `next`. Synchronizing those methods would not achieve much, because after one thread has called `hasNext` and determined that it returns `true`, another thread might come in an call `next`, consuming the current value, which might be the last one. So the first thread will then call `next` and throw an exception because the iterator is empty. An iterator is absolutely not the right abstraction to share among several threads. – Régis Jean-Gilles Jun 04 '15 at 10:58
  • So what would you suggest? I need each thread that accesses a collection to get the next valid value. What other options do I have? – joniba Jun 04 '15 at 11:16
  • Without much information about your particular use case, I'd say maybe using something like `java.util.concurrent.ConcurrentLinkedQueue` by example. – Régis Jean-Gilles Jun 04 '15 at 11:20
  • I'm not seeing any way to make this behave circularly? – joniba Jun 04 '15 at 12:08
  • You can use [CircularFifoBuffer](https://commons.apache.org/proper/commons-collections/javadocs/api-3.2.1/org/apache/commons/collections/buffer/CircularFifoBuffer.html) from ApacheCommons in a [synchronized way](http://stackoverflow.com/questions/11079210/thread-safe-circular-buffer-in-java) – shutty Jun 04 '15 at 13:04

2 Answers2

3

I've ran into a same question but I think we can do this to be safe as implementation independent as discussed here.

iterator.synchronized(
  iterator.next()
)
jk-kim
  • 1,136
  • 3
  • 12
  • 20
  • The problem with this approach is that not only is hasNext still not reëntrant but it's not atomic with next, i.e. hasNext could return true but some other thread could consume that last element before the current thread can take the element (at which point it'll get a null and then, likely, an NPE). – F. P. Freely Jun 04 '18 at 20:03
1

Just wrap to the Provider. smth like this:

class Iterator2ProviderAdapter[A](iterator: Iterator[A]) {
    def get: A = synchronized(iterator.next())
}
Alexandr P
  • 11
  • 1