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