- Process everything serially -> bad scalability
- Iterating not safe, since not atomic -> need to block during iteration or copy
- Compounded by hidden iterators like
toString
- Allow concurrent access -> much faster
- Weakly consistent iterators that won't throw
ConcurrentModificationException
- Weaker guarantees by
getSize
andisEmpty
- Weaker guarantees by
- Provide more high level, atomic operations such as
putIfAbsent
- Cost of copy might comes at cost. Reasonable if iterating far more than writing
- Allows for easy implementation of producer/consumer pattern
- Consumer can keep reading and will wait automatically
- Write can keep writing
offer
fails if it cannot write (nice for writing to disk etc.)- Great for throttling operations that otherwise would overwhelm the system
- Various implementations to allow for different orders in the queue (PriorityBlockingQueue, ArrayBlockingQueue, etc.)
- SynchronousBlockingQueue
- threads wait for work, rather than work for threads
- Makes for safe object hand-off (serial thread confinement)
Example: desktop search - Finding files and then indexing them
- Queue with two ends "head" and "tail"
- Consumer can take from tail of other's deque if empty
- Good for cases where consumer is also producer
Does InterruptedException
indicate that the task should be aborted?
Have some state and make threads wait till some conditions around that state
- Starts closed and flips to open. Once opened cannot close.
CountdownLatch
- Blocks till count hits 0- See Testing example: We wait for all threads to be ready (
CountdownLatch(n)
) then run work in parallel and wait with another latch for completion
- See Testing example: We wait for all threads to be ready (
start
method kicks of processingget
blocks till done and then returns answer- Exceptions get returned as Throwable -> painful to unpack
- Why do we throw a IllegalStateException if it's not a
RuntimeException
?
- As long as counter is > 0 workers can proceed
- Like Latch but
- Can be reused
- all threads most come together at same time
- Great for simulations (see game of life)
- Synchronize reads -> slow because everything blocks. might be slower than without memoization
- ConcurrentHashMap -> scales better, but computation can happen in parallel
- Put Future in HashMap -> Unfortunate timing can still result in two calculations at same time
- Use
putIfAbsent
to put Future -> eliminates chance of double-computation