Concurrency 3 Bloch
Concurrency 3 Bloch
Part 3: Concurrency
17-214 1
Administrivia
17-214 2
Key concepts from Thursday
17-214 3
Lock splitting for increased concurrency
Review: what’s the bug in this code?
b
d
a e
c
f g
h
i
17-214 5
Avoiding deadlock by ordering lock acquisition
17-214 7
Java Concurrency in Practice annotations
17-214 8
Today
17-214 9
Policies for thread safety
Remember: shared mutable state must be synchronized
17-214 10
1. Thread-confined state - three kinds
• Stack-confined
– Primitive local variables are never shared between threads
– Fast and cheap
• Unshared object references
– The thread that creates an object must take action to share (“publish”)
– e.g., put it in a shared collection, store it in a static variable
• Thread-local variables
– Shared object with a separate value for each thread
– Rarely needed but invaluable (e.g., for user ID or transaction ID)
class ThreadLocal<T> {
ThreadLocal() ; // Initial value for each thread is null
static <S> ThreadLocal<S> withInitial
(Supplier<S> supplier);
void set(T value); // Sets value for current thread
T get(); // Gets value for current thread
}
17-214 11
2. Shared read-only state
17-214 12
3. Shared thread-safe state
17-214 13
4. Shared guarded state
17-214 14
Outline
17-214 15
wait/notify – a primitive for cooperation
The basic idea is simple…
17-214 16
But the devil is in the details
Never invoke wait outside a loop!
17-214 17
All of your waits should look like this
synchronized (obj) {
while (<condition does not hold>) {
obj.wait();
}
17-214 18
Why can a thread wake from a wait when condition
does not hold?
• Another thread can slip in between notify & wake
• Another thread can invoke notify accidentally or maliciously
when condition does not hold
– This is a flaw in Java locking design!
– Can work around flaw by using private lock object
• Notifier can be liberal in waking threads
– Using notifyAll is good practice, but can cause extra wakeups
• Waiting thread can wake up without a notify(!)
– Known as a spurious wakeup
17-214 19
Defining your own thread-safe objects
17-214 20
A toy example: Read-write locks (a.k.a. shared/exclusive locks)
lock.readLock();
try {
// Do stuff that requires read (shared) lock
} finally {
lock.unlock();
}
lock.writeLock();
try {
// Do stuff that requires write (exclusive) lock
} finally {
lock.unlock();
}
17-214 21
A toy example: Read-write locks (implementation 1/2)
17-214 22
A toy example: Read-write locks (implementation 2/2)
17-214 23
Advice for building thread-safe objects
17-214 24
Documentation
17-214 25
Summary of our RwLock example
17-214 26
Outline
17-214 27
java.util.concurrent is BIG (1)
17-214 28
java.util.concurrent is BIG (2)
17-214 29
1. Overview of java.util.concurrent.atomic
• Atomic{Boolean,Integer,Long}
– Boxed primitives that can be updated atomically
• AtomicReference<T>
– Object reference that can be updated atomically
• Atomic{Integer,Long,Reference}Array
– Array whose elements may be updated atomically
• Atomic{Integer,Long,Reference}FieldUpdater
– Reflection-based utility enabling atomic updates to volatile fields
– Advanced/obscure. Offers space performance in exchange for ugliness.
• LongAdder, DoubleAdder
– Highly concurrent sums – use case: parallel statistics gathering
• LongAccumulator, DoubleAccumulator
– Generalization of adder to arbitrary functions (max, min, etc.)
17-214 30
Example: AtomicLong
class AtomicLong { // We used this in generateSerialNumber()
long getAndIncrement(); // We used this method
long get();
void set(long newValue);
long getAndSet(long newValue);
long getAndAdd(long delta);
boolean compareAndSet(long expectedValue, long newValue);
long getAndUpdate(LongUnaryOperator updateFunction);
long updateAndGet(LongUnaryOperator updateFunction);
…
}
17-214 31
2. Concurrent collections
Unsynchronized Concurrent
HashMap ConcurrentHashMap
HashSet ConcurrentHashSet
TreeMap ConcurrentSkipListMap
TreeSet ConcurrentSkipListSet
17-214 32
You can’t prevent concurrent use of a concurrent collection
synchronized(syncMap) {
if (!syncMap.containsKey("foo"))
syncMap.put("foo", "bar");
}
• But not for concurrent collections
– They do their own internal synchronization
– Acquiring intrinsic lock will not exclude concurrent activity
– Never synchronize on a concurrent collection!
17-214 33
Instead, use atomic read-modify-write methods
17-214 34
Concurrent collection example: canonicalizing map
public T intern(T t) {
String previousValue = map.putIfAbsent(t, t);
return previousValue == null ? t : previousValue;
}
17-214 35
java.util.concurrent.ConcurrentHashMap
Locks
Hash table
17-214 36
Aside: the producer-consumer pattern
17-214 37
3. Data exchange collections summary
Hold elements for processing by another thread (producer/consumer)
17-214 38
Summary of BlockingQueue methods
17-214 39
Summary of BlockingDeque methods
17-214 40
4. Executor framework overview
17-214 41
Executors – your one-stop shop for executor services
• Executors.newSingleThreadExecutor()
– A single background thread
• newFixedThreadPool(int nThreads)
– A fixed number of background threads
• Executors.newCachedThreadPool()
– Grows in response to demand
17-214 42
A very simple (but useful) executor service example
17-214 43
Other things you can do with an executor service
17-214 44
The fork-join pattern
17-214 45
ForkJoinPool: executor service for ForkJoinTask
Dynamic, fine-grained parallelism with recursive task splitting
class SumOfSquaresTask extends RecursiveAction {
final long[] a; final int lo, hi; long sum;
SumOfSquaresTask(long[] array, int low, int high) {
a = array; lo = low; hi = high;
}
17-214 46
5. Overview of synchronizers
• CountDownLatch
– One or more threads wait for others to count down from n to zero
• CyclicBarrier
– a set of threads wait for each other to be ready (repeatedly if desired)
• Semaphore
– Like a lock with a maximum number of holders (“permits”)
• Phaser – Cyclic barrier on steroids
– Extremely flexible and complex
• AbstractQueuedSynchronizer – roll your own!
17-214 47
6. Overview of java.util.concurrency.locks (1/2)
• ReentrantReadWriteLock
– Shared/Exclusive mode locks with tons of options
• Fairness policy
• Lock downgrading
• Interruption of lock acquisition
• Condition support
• Instrumentation
• ReentrantLock
– Like Java’s intrinsic locks
– But with more bells and whistles
17-214 48
Overview of java.util.concurrency.locks (2/2)
• Condition
– wait/notify/notifyAll with multiple wait sets per object
• AbstractQueuedSynchronizer
– Skeletal implementation of locks relying on FIFO wait queue
• AbstractOwnableSynchronizer,
AbstractQueuedLongSynchronizer
– Fancier skeletal implementations
17-214 49
ReentrantReadWriteLock example
Does this look vaguely familiar?
rwl.readLock().lock();
try {
// Do stuff that requires read (shared) lock
} finally {
rwl.readLock().unlock();
}
rwl.writeLock().lock();
try {
// Do stuff that requires write (exclusive) lock
} finally {
rwl.writeLock().unlock();
}
17-214 50
Summary
17-214 51