Notes of Learning Concurrent Programming in Scala
basic
processes and threads
- 1 : 1
- deterministic / nondeterministic
thread states
- new
- runnable
- running
- waiting
- terminated
monitors and synchronization
- monitor locks aka intrinsic locks
- synchronized statements
- wait and notify
- deadlocks
- guarded blocks
- interrupting threads and graceful shutdown
lock optimizations
- adaptive spinning
- lock elimination
- lock coarsening
- lightweight locking
- biased locking
volatile variables
- reordering
- visible
memory model
- sequential consistency
- happens before
immutable objects
- final fields
traditional building blocks
thread pools
- Executor
- ExecutorService
- ExecutionContext
ForkJoinPool
starvation
atomic variables
- compare and swap (cas)
- linearizable and atomic
- lock-free programming
- implementing locks explicitly
- the aba problem
(store immutable values inside the atomic references)
lazy values
- double-checked locking idiom
(never invoke blocking operations inside lazy value initialization expressions)
(never call synchronized on publicly available objects; always use a dedicated, private dummy object for synchronization)
concurrent collections
concurrent queues
- BlockingQueue
- ArrayBlockingQueue (bounded)
- LinkedBlockingQueue (unbounded)
operation | enqueue | dequeue | inspect |
---|---|---|---|
exception | add | remove | element |
special value | offer | poll | peek |
timed | offer | poll | |
blocking | put | take |
- weakly consistent iterators
(use iterators on concurrent data structures only when you can ensure that no other thread will modify the data structure from the point where the iterator was created until the point where the iterator's hasnext method returns false)
concurrent maps
- concurrent Map
- ConcurrentHashMap asScala
- ConcurrentSkipListSet asScala
complex linearizable methods
- putIfAbsent
- remove/2
- replace/2
- replace/3
(equivalent on 'equals method')
linearizable methods
- +=
- -=
- put
- update
- get
- apply
- remove
(avoid using the null value as a key or a value in a concurrent data structure)
concurrent traversals
- TrieMap
(use TrieMap if you require consistent iterators and ConcurrentHashMap when the get and apply operations are the bottlenecks in your program)
processes
asynchronous
futures
- apply
- foreach
- onComplete
- failed
- map
- flatMap
promises
- apply
- success
- failure
- complete
tryComplete
cancellation
await
- ready
- result
- blocking (BlockContext)
async
data parallel collections
- trivially parallelizable
- modified exclusive shared invalid (mesi)
- resource contention
- memory contention
(writing to the same memory location with proper synchronization leads to performance bottlenecks and contention; avoid this in data-parallel operations)
hierarchy
- GenTraversable
- GenIterable
- ParIterable
- ParSeq
- ParMap
- ParSet
parallelism level
- TaskSupport
measuring performance
- interpreted mode
- steady state
caveats
parallelizable collections
- Range
- Vector
- HashMap
- HashSet
- mutable Array
- mutable ArrayBuffer
- mutable HashMap
- mutable HashSet
- concurrent TrieMap
parallelizable operations
- fold
- reduce
- aggregate
non-parallelizable operations
- foldLeft
- foldRight
- reduceLeft
- reduceRight
- reduceLeftOption
- reduceRightOption
- scanLeft
- scanRight
- toList
- toStream
side effects
(to avoid the need for synchronization and ensure better scalability, favor declarative-style parallel operations instead of the side effects in parallel for loops)
nondeterministic parallel operations
- find
associative operators
- monoid
implementing custom parallel collections
- immutable ParSeq
- Splitter
- IterableSplitter
- SeqSplitter
- Combiner
reactive extensions
observables
- cold observables
- hot observables
constructors
- from
- just
- timer
- interval
- error
- create
- apply
transformers
- map
- filter
- take
- concat
- flatten
- flatMap
- timeout
- merge
- retry
- repeat
- scan
- onErrorReturn
- onErrorResumeNext
- observeOn
accessors
- subscribe
observers
- onNext
- onError
- onCompleted
subscriptions
- apply
- unsubscribe
schedulers
- ComputationScheduler
- IOScheduler
- NewThreadScheduler
- Schedulers from
subjects
- Subject
- ReplaySubject
- BehaviorSubject
AsyncSubject
top-down programming style
software transactional memory
- memory transactions
- isolation
- composability
interaction between transactions and side effects
(only use the transactional context within the thread that started the transaction)
(avoid external side effects inside the transactions, as the transactions can be re-executed multiple times)
(use the Txn's afterCommit and afterRollback methods to perform side-effecting operations in the transactions without the danger of executing them multiple times)
single-operation transactions
(use single-operation transactions for single read, write, and cas-like operations in order to avoid the syntactic boilerplate associated with the atomic blocks)
nesting transactions
(nested atomic blocks result in a transaction that starts when the top-level atomic block starts, and can commit only after the top-level atomic block completes)
transactions and exceptions
(when an exception is thrown inside a transaction, the transaction is rolled back and the exception is rethrown at the point where the top-level atomic block started)
retrying transactions
(avoid long-running transactions whenever possible. never execute an infinite loop inside a transaction, as it can cause deadlocks)
(use the retry statement to block the transaction until a specific condition is fulfilled, and retry the transaction automatically once its read set changes)
retrying with timeouts
(when a timeout represents exceptional program behavior, use the withRetryTimeout method to set the timeout duration in the transaction; when the transaction proceeds normally after a timeout, use the retryFor method)
transactional collections
- transaction-local variable
- transactional arrays
- transactional maps
actor
ops on actor-ref
!
tell / bang- forward
?
ask (pattern)- pipeTo (pattern)
ops on system
- actorOf
- actorSelection
- terminate
ops on context
- system
- props
- self
- sender
- children
- parent
- become
- watch
- stop
messages
- Identify
- ActorIdentity
- Kill
- PoisonPill
- Terminated
unhandled
life cycles
- preStart
- postStop
- preRestart
- postRestart
supervisions
- Restart
- Resume
- Stop
- Escalate
strategies
- OneForOne
- AllForOne
concurrency in practice
(there is no one-size-fits-all technology, use your own best judgment when deciding which concurrency framework to use for a specific programming task)
utilities
- jvisualvm
- scalameter