This presentation introduces the concept of synchronization beatween threads, as implemented in the Java platform. It is the second part of a series of slides dedicated to thread synchronization. This slides introduces the following concepts:
- Conditional locking
- Volatile variables
- Thread confinement
- Immutability
The presentation is took from the Java course I run in the bachelor-level informatics curriculum at the University of Padova.
1. CONCURRENT PROGRAMMING
SYNCHRONIZATION (PART 2)
PROGRAMMAZIONE CONCORRENTE E DISTR.
Università degli Studi di Padova
Dipartimento di Matematica
Corso di Laurea in Informatica, A.A. 2015 – 2016
rcardin@math.unipd.it
3. Programmazione concorrente e distribuita
CONDITIONS
Condition variables
Often a thread enters a critical section only to
discover that it can’t proceed
A condition is not fulfilled
We can try to use a lock
3Riccardo Cardin
if (bank.getBalance(from) >= amount) {
// Thread might be deactivated at this point
bank.transfer(from, to, amount);
}
public void transfer(int from, int to, int amount) {
bankLock.lock();
try {
while (accounts[from] < amount) {
// wait
}
// transfer funds
}
No thread can
withdraw money, due
to the acquired lock:
DEADLOCK!!
4. Programmazione concorrente e distribuita
CONDITIONS
To avoid unpleasant deadlock, use conditions
A condition variable is built from a lock
A thread owning the lock, calls await on the condition
The lock is released by the thread
Thread is not made runnable when the lock i available. It stays
deactivated until the condition will be fulfilled
Wait set for the condition
4Riccardo Cardin
class Bank {
private Condition sufficientFunds;
public Bank() {
// Getting a condition with an evocative name
sufficientFunds = bankLock.newCondition();
}
}
sufficientFunds.await();
5. Programmazione concorrente e distribuita
CONDITIONS
When another thread fulfills the condition, it
should notify other awaiting threads
One of the awaiting thread will be eligible to acquire
the lock and to continue where it left off
Lock must be available
The condition may be fulfilled
Retry to check that condition are met over and over again
An awaiting thread cannot reactive itself: be carefull!
5Riccardo Cardin
sufficientFunds.signalAll();
while (!(/* ok to proceed */)) {
condition.await();
}
6. Programmazione concorrente e distribuita
CONDITIONS
It’s important that some thread calls the
signalAll method eventually
If no other thread bother to reactivate a waiting thread,
it will neve run again
DEADLOCK!
Call signalAll whenever the state of an object changes
6Riccardo Cardin
public void transfer(int from, int to, int amount) {
bankLock.lock();
try {
while (accounts[from] < amount)
sufficientFunds.await();
// transfer funds
sufficientFunds.signalAll();
} finally {
bankLock.unlock();
} }
8. Programmazione concorrente e distribuita
CONDITIONS
Intrinsic locks have a single associated condition
The wait method adds a thread to the wait set
The notifyAll method unblocks waiting threads
Having a single condition per intrinsic lock can be
inefficient
Which condition has been safisfied? All threads waiting have
to be resumed
8Riccardo Cardin
public synchronized void transfer(int from, int to, int amount)
throws InterruptedException {
while (accounts[from] < amount)
wait(); // wait on intrinsic object lock
// transfer funds
notifyAll(); // notify all threads waiting
}
9. Programmazione concorrente e distribuita
CONDITIONS PITFALLS
What should you use in your code, Locks or
synchronized methods
Neither. In many situation it can be used one of the
mechanisms of the java.util.concurrent package
i.e. – Blocking queues
If you have to choose, use synchronized blocks
Use Lock / Condition if you really need the
additional power that gives to you
You have to define a custom protocol of synchronization
9Riccardo Cardin
Do not underestimate the powers of the dark side of concurrency
-- Riccardo Cardin
10. Programmazione concorrente e distribuita
VOLATILE VARIABLES
Cached values and operations reodering are evil!
A volatile variable is not cached by threads
Share the visibility feature of synchronized
Threads will automatically see the most up-to-date value
...but non of the atomicity features
Possible race-conditions on multiple operations
10Riccardo Cardin
If you write a variable which may next be read by another thread, or
you read a variable which may have last been written by another
thread, you must use synchronization.
-- Brian Goetz
private volatile boolean done;
public boolean isDone() { return done; }
public void setDone() { done = true; }
11. Programmazione concorrente e distribuita
VOLATILE VARIABLES
When to use volatile vars instead of locks
Writes do not depend on its current value
DO NOT use for implementing counters!
The variable does not partecipate in invariants with
other variables
Slightly better performances
11Riccardo Cardin
// Not atomic, you need synchronization
public void flipDone() { done = !done; }
volatile boolean shutdownRequested;
public void shutdown() { shutdownRequested = true; }
public void doWork() {
while (!shutdownRequested) {
// do stuff
}
}
Pattern of use:
status flag
13. Programmazione concorrente e distribuita
ATOMICS
There are operations other than setter and
getter provided by volatile variables
java.util.concurrent.atomic provides classes
that guarantee atomicity of other operations
AtomicInteger, AtomicBoolean, AtomicLong, ...
13Riccardo Cardin
class AtomicCounter {
private AtomicInteger c = new AtomicInteger(0);
public void increment() {
c.incrementAndGet();
}
public void decrement() {
c.decrementAndGet();
}
public int value() {
return c.get();
}
}
Uses low level CPU
operations, that don’t need
synchronization (CAS,
compare-and-swap)
14. Programmazione concorrente e distribuita
THREAD CONFINEMENT
The best solution to concurrency problems is to
not share any mutable state
Use ThreadLocal helper class to give each thread an
instance of a class
When thread terminates, value is garbage collected
Do not use as a replacement for global variables
Many JDK classes are not thread-safe
SimpleDateFormat, Random, ...
14Riccardo Cardin
public static final ThreadLocal<SimpleDateFormat> dateFormat =
new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}};
String dateStamp = dateFormat.get().format(new Date());
15. Programmazione concorrente e distribuita
IMMUTABILITY
All the problems described so far have to do
with accessing shared mutable state
If object state cannot be modified, the risks go away
Immutable object are simple
There are not different states for complex objects
Immutable object are safer
No untrusted code can modify directly object’s state or retain
a reference to modify it later
Java does not formally defined immutability
It is not sufficient declaring all fields as final
15Riccardo Cardin
Immutable objects are always thread-safe
-- Brian Goetz
16. Programmazione concorrente e distribuita
IMMUTABILITY
An object is immutable if:
Its state cannot be modified after construction
So a immutable class has reference only to (effectively)
immutable classes
All its fields are final
It is properly constructed
The this reference does not escape during construction, i.e.
calling code outside the class, and passing this
Can use mutable state for internal representation
Are this kind of object useful?
There is a big difference between an object been immutbale
and the reference to it being immutable
16Riccardo Cardin
18. Programmazione concorrente e distribuita
IMMUTABILITY
The final keyword on fields makes possibile
the guarantee on initialization safety
A more limited version of the const in C++
No reorder will be done by the compiler
So, final fields can be accessed without additional
synchronization
Better maintainability
It’s time to have a look to an immutable class!
18Riccardo Cardin
Immutable objects can be used safely by any thread without additional
synchronization.
-- Brian Goetz
19. Programmazione concorrente e distribuita
IMMUTABILITY
19Riccardo Cardin
class OneValueCache {
private final BigInteger lastNumber;
private final BigInteger[] lastFactors;
// Do not use directly a mutable object to construct
// an immutable object
public OneValueCache(BigInteger i, BigInteger[] factors) {
lastNumber = i;
lastFactors = Arrays.copyOf(factors, factors.length);
}
// Do not late ‘escape’ an internal value of the immutable
// object. In this way no other code can maliciously modify
// that state
public BigInteger[] getFactors(BigInteger i) {
if (lastNumber == null || !lastNumber.equals(i))
return null;
else
return Arrays.copyOf(lastFactors, lastFactors.length);
}
}
}
21. Programmazione concorrente e distribuita
REFERENCES
Chap. 14 «Multithreading», Core Java Volume I - Fundamentals, Cay
Horstmann, Gary Cornell, 2012, Prentice Hall
Chap. 3 «Sharing Objects», Java Concurrency in Practice, Brian
Goetz, 2006, Addison-Wesley Professional
Atomic Access
https://docs.oracle.com/javase/tutorial/essential/concurrency/ato
mic.html
Java theory and practice: Managing volatility
http://www.ibm.com/developerworks/library/j-jtp06197/
Java theory and practice: Going atomic
http://www.ibm.com/developerworks/library/j-jtp11234/
What is the difference of Atomic / Volatile / synchronize?
http://stackoverflow.com/questions/9749746/what-is-the-
difference-of-atomic-volatile-synchronize
21Riccardo Cardin