KEMBAR78
Java- Concurrent programming - Synchronization (part 1) | PPTX
CONCURRENT PROGRAMMING
SYNCHRONIZATION (PART 1)
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
Programmazione concorrente e distribuita
SUMMARY
 Introduction
 Thread-safety
 Race conditions
 Locking
 Locking pitfalls
2Riccardo Cardin
Programmazione concorrente e distribuita
INTRODUCTION
 Threads can reduce development cost and
improve the performance application
 Exploiting multiple processors
 Improved throughput by utilizing available processors
 Simplicity of modeling
 Use every thread to do a specific task is simplier than using
one thread to do them all
 Simplified handling of asynchronous events
 Prevention of server’s stall while it is fulfilling a request
 More reponsive user interfaces
 Use of different threads for GUI and event management
 Event dispatch thread (EDT)
3Riccardo Cardin
Programmazione concorrente e distribuita
INTRODUCTION
 Risks of threads
 Safety hazards
 Without sufficient synchronization, the ordering of
operations in multiple threads is unpredictable
4Riccardo Cardin
public class UnsafeSequence {
private int value;
/** Returns a unique value. */
public int getNext() {
return value++; // Three operations: read, add and store
}
}
With unlucky timing, two
threads could call
getNext and receive the
same value
Programmazione concorrente e distribuita
INTRODUCTION
 Risks of threads
 Liveness hazards
 An activity gets into a state such that it is permanently unable
to make forward progress
 Dining philosophers problem
 Performance hazards
 Poor service time, responsiveness, throughput,
resource consumption, or scalability
 Context switch is not cost free
5Riccardo Cardin
If thread A is waiting for a resource that thread B holds
exclusively, and B never releases it, A will wait forever.
Programmazione concorrente e distribuita
INTRODUCTION
6Riccardo Cardin
Programmazione concorrente e distribuita
THREAD SAFETY
 Writing thread-safe code is about managing
access to shared and mutable state
 Object state is represented by its data
 By Shared, we mean that a variable could be
accessed by multiple threads
 By mutable, that its value could change
 It is far easier to design a class to be thread-safe than
to retrofit it later
7Riccardo Cardin
Whenever more than a thread accesses a given state variable, and one
of them might write to it, they all must coordinate their access to it
using synchronization
-- Brian Goetz
Programmazione concorrente e distribuita
THREAD SAFETY
 There are three ways to fix a mutable shared
variable
 Don’t share the state variable across threads
 Make the state variable immutable
 Use synchronization whenever accessing it
 Object oriented techniques favor thread safety
 Encapsulation
 Immutability
 Clear specification of invariants
8Riccardo Cardin
Stateless objects are always thread-safe.
-- Brian Goetz
Programmazione concorrente e distribuita
THREAD SAFETY
9Riccardo Cardin
Programmazione concorrente e distribuita
RACE CONDITIONS
 Race conditions
 The correctness of a computation depends on
relative timing of multiple threads at runtime
 Atomicity
 A set of statements is not atomic if they are not executed in a
single, indivisible operation
 Read-modifiy-write
 Check-then-act
10Riccardo Cardin
A class is thread-safe if it behaves correctly when accessed from
multiple threads, regardless of the scheduling or interleaving of the
execution of those threads by the runtime environment.
-- Brian Goetz
Programmazione concorrente e distribuita
RACE CONDITIONS
 Read-modify-write race condition
 The value of value is read, then modified adding 1
and finally stored into value variable
 Among the execution of every statement, control flow could
be preempted by another thread
11Riccardo Cardin
public class UnsafeSequence {
private int value;
/** Returns a unique value. */
public int getNext() {
value = value + 1;
return value;
}
}
read value value + 1 store value
Possible preemption
Programmazione concorrente e distribuita
RACE CONDITIONS
 Check-then-act race condition
 Lazy initialization
 The boolean expression depends on a value that is
modified according to it
12Riccardo Cardin
public class LazyInitRace {
private ExpensiveObject instance = null;
public ExpensiveObject getInstance() {
// Check-then-act
if (instance == null)
instance = new ExpensiveObject();
return instance;
}
}
check instance create object store instance
Possible preemption
Programmazione concorrente e distribuita
RACE CONDITIONS
 Compound actions
 Sequences of operations that must be executed
atomically in order to remain thread-safe.
 Read-modify-write and Check-then-act must always be
atomic to be thread-safe
 Atomicity is relative to operation that are executed
on shared state
 The java.util.concurrent.atomic package contains
atomic variable classes for effecting atomic state transitions
13Riccardo Cardin
public class SafeSequence {
private AtomicInteger value;
public int getNext() {
return value.incrementAndGet(); // Atomic read-modify-write
}
}
Programmazione concorrente e distribuita
RACE CONDITIONS
14Riccardo Cardin
Programmazione concorrente e distribuita
SHARING STATE
 Writing correct concurrent programs is primarily
about managing access to shared, mutable state
 It’s all about memory visibility
 We want to ensure that when a thread modifies the state of
an object, other threads can actually see those changes
 If shared state is represented by more than one variable,
atomic classes are not useful
15Riccardo Cardin
public class UnsafeCachedSequence {
private AtomicInteger lastValue;
private AtomicInteger value;
public int getNext() {
// Invariant of the class is not satisfied anymore
lastValue.set(value.get());
return value.incrementAndGet();
}
}
Programmazione concorrente e distribuita
LOCKING
 To preserve state consistency, update related
state variable in a single atomic operation
 Java has a built-in locking mechanism for enforcing
atomicity: synchronized block
 But, it is easier to understand the synchronized
keyword after having seen locks in isolation...
16Riccardo Cardin
synchronized (lock) {
// Access or modify shared state guarded by lock
}
Object that will serve
as lock
Block code to be guarded by
the lock
Programmazione concorrente e distribuita
LOCKING
 Reentrant locking
 Use a Lock to protect a code block
 The construct guarantees that only one thread at time
can enter the critical section
 Always release the lock in a finally block to prevents deadlocks
 The class ReentranctLock implements basic functionalities of
a lock
17Riccardo Cardin
myLock.lock(); // a ReentrantLock object
try {
// Operation in this block are executed atomically
} finally {
// make sure the lock is unlocked even if an
// exceptions thrown
myLock.unlock();
}
Programmazione concorrente e distribuita
LOCKING
 Reentrant locking
 The lock acts as mutual exclusion locks
18Riccardo Cardin
public class SafeCachedSequence {
private Lock lock = new ReentrantLock();
private int lastValue;
private int value;
public int getNext() {
// Invariant of the class is now satisfied
lock.lock();
try {
lastValue = value;
value = value + 1;
int result = value;
} finally {
lock.unlock();
}
return result;
}
}
Now the invariant of
the class is
guaranteed by the
lock: value and
lastValue will always
be updated in a
consistent way
Programmazione concorrente e distribuita
LOCKING
 Reentrant locking
19Riccardo Cardin
Thread 1 Thread 2
getNext getNext
Unsynchronized
Thread 1 Thread 2
getNext
getNext
Synchronized
Programmazione concorrente e distribuita
LOCKING
 Reentrant locking
 Different threads have to synchronize using the same
instance of the lock
 It is called reetrant because a thread can repeatedly
acquire a lock that it already owns
 The lock has a hold count that keeps track of the nested calls
to the lock method
 Prevents deadlocks wrt the subclass mechanism
 Every object in Java (since 1.0) has an intrinsic lock
 The synchronized keyword on a method protects the
access to that method using this reference as lock object
 All method’s code is guarded
20Riccardo Cardin
Programmazione concorrente e distribuita
LOCKING
 Intrinsic locking
 To call the method, a thread must acquire the
intrinsic object lock
 Static synchronized methods use the Class object as lock.
21Riccardo Cardin
public synchronized void method() {
// method body
}
// ...is equivalent to
public void method() {
this.intrinsicLock.lock();
try {
// method body
} finally {
this.intrinsicLock.unlock();
}
}
Programmazione concorrente e distribuita
LOCKING
 Intrinsic locking suffers of performance issues
 Use synchronization by Lock object
 Or synchronized block
 It uses the reference to an object that will serve as lock
22Riccardo Cardin
public class SafeCachedSequence {
private Object lock = new Object();
private int lastValue;
private int value;
public int getNext() {
synchronized (lock) {
lastValue = value;
value = value + 1;
int result = value;
}
return result;
}
}
Programmazione concorrente e distribuita
LOCKING
23Riccardo Cardin
Programmazione concorrente e distribuita
LOCKING PITFALLS
 All the accesses to a mutable shared variable
must be performed with the same lock held
 Not only compound actions
 Not all data needs to be guarded by a lock
 Only mutable data
 All the variables involved in the same invariant
must be guarded by the same lock
 It is not sufficient to use intrinsic lock on every
method
24Riccardo Cardin
// Not thread-safe
if (!vector.contains(element))
vector.add(element);
Programmazione concorrente e distribuita
LOCKING PITFALLS
 Poor concurrency
 Limits by the availability of processing resources, not
by the structure of application itself
 CPU intensive and I/O operations must be outside
synchronized blocks
 Acquiring and releasing a lock has some overhead
 Not break down synchronized blocks too far
25Riccardo Cardin
There is frequently a tension between simplicity and performance.
When implementing a synchronization policy, resist the temptation to
prematurely sacrifice simplicity (potentially compromising safety) for
the sake of performance.
-- Brian Goetz
Programmazione concorrente e distribuita
EXAMPLES
26Riccardo Cardin
https://github.com/rcardin/pcd-snippets
Programmazione concorrente e distribuita
REFERENCES
 Chap. 1 «Introduction», Java Concurrency in Practice, Brian Goetz,
2006, Addison-Wesley Professional
 Chap. 2 «Thread Safety», Java Concurrency in Practice, Brian Goetz,
2006, Addison-Wesley Professional
 Chap. 3 «Sharing Objects», Java Concurrency in Practice, Brian
Goetz, 2006, Addison-Wesley Professional
 Dining philosophers problem
https://en.wikipedia.org/wiki/Dining_philosophers_problem
 Chap. 14 «Multithreading», Core Java Volume I - Fundamentals, Cay
Horstmann, Gary Cornell, 2012, Prentice Hall
 Intrinsic Locks and Synchronization
https://docs.oracle.com/javase/tutorial/essential/concurrency/lock
sync.html
27Riccardo Cardin

Java- Concurrent programming - Synchronization (part 1)

  • 1.
    CONCURRENT PROGRAMMING SYNCHRONIZATION (PART1) 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
  • 2.
    Programmazione concorrente edistribuita SUMMARY  Introduction  Thread-safety  Race conditions  Locking  Locking pitfalls 2Riccardo Cardin
  • 3.
    Programmazione concorrente edistribuita INTRODUCTION  Threads can reduce development cost and improve the performance application  Exploiting multiple processors  Improved throughput by utilizing available processors  Simplicity of modeling  Use every thread to do a specific task is simplier than using one thread to do them all  Simplified handling of asynchronous events  Prevention of server’s stall while it is fulfilling a request  More reponsive user interfaces  Use of different threads for GUI and event management  Event dispatch thread (EDT) 3Riccardo Cardin
  • 4.
    Programmazione concorrente edistribuita INTRODUCTION  Risks of threads  Safety hazards  Without sufficient synchronization, the ordering of operations in multiple threads is unpredictable 4Riccardo Cardin public class UnsafeSequence { private int value; /** Returns a unique value. */ public int getNext() { return value++; // Three operations: read, add and store } } With unlucky timing, two threads could call getNext and receive the same value
  • 5.
    Programmazione concorrente edistribuita INTRODUCTION  Risks of threads  Liveness hazards  An activity gets into a state such that it is permanently unable to make forward progress  Dining philosophers problem  Performance hazards  Poor service time, responsiveness, throughput, resource consumption, or scalability  Context switch is not cost free 5Riccardo Cardin If thread A is waiting for a resource that thread B holds exclusively, and B never releases it, A will wait forever.
  • 6.
    Programmazione concorrente edistribuita INTRODUCTION 6Riccardo Cardin
  • 7.
    Programmazione concorrente edistribuita THREAD SAFETY  Writing thread-safe code is about managing access to shared and mutable state  Object state is represented by its data  By Shared, we mean that a variable could be accessed by multiple threads  By mutable, that its value could change  It is far easier to design a class to be thread-safe than to retrofit it later 7Riccardo Cardin Whenever more than a thread accesses a given state variable, and one of them might write to it, they all must coordinate their access to it using synchronization -- Brian Goetz
  • 8.
    Programmazione concorrente edistribuita THREAD SAFETY  There are three ways to fix a mutable shared variable  Don’t share the state variable across threads  Make the state variable immutable  Use synchronization whenever accessing it  Object oriented techniques favor thread safety  Encapsulation  Immutability  Clear specification of invariants 8Riccardo Cardin Stateless objects are always thread-safe. -- Brian Goetz
  • 9.
    Programmazione concorrente edistribuita THREAD SAFETY 9Riccardo Cardin
  • 10.
    Programmazione concorrente edistribuita RACE CONDITIONS  Race conditions  The correctness of a computation depends on relative timing of multiple threads at runtime  Atomicity  A set of statements is not atomic if they are not executed in a single, indivisible operation  Read-modifiy-write  Check-then-act 10Riccardo Cardin A class is thread-safe if it behaves correctly when accessed from multiple threads, regardless of the scheduling or interleaving of the execution of those threads by the runtime environment. -- Brian Goetz
  • 11.
    Programmazione concorrente edistribuita RACE CONDITIONS  Read-modify-write race condition  The value of value is read, then modified adding 1 and finally stored into value variable  Among the execution of every statement, control flow could be preempted by another thread 11Riccardo Cardin public class UnsafeSequence { private int value; /** Returns a unique value. */ public int getNext() { value = value + 1; return value; } } read value value + 1 store value Possible preemption
  • 12.
    Programmazione concorrente edistribuita RACE CONDITIONS  Check-then-act race condition  Lazy initialization  The boolean expression depends on a value that is modified according to it 12Riccardo Cardin public class LazyInitRace { private ExpensiveObject instance = null; public ExpensiveObject getInstance() { // Check-then-act if (instance == null) instance = new ExpensiveObject(); return instance; } } check instance create object store instance Possible preemption
  • 13.
    Programmazione concorrente edistribuita RACE CONDITIONS  Compound actions  Sequences of operations that must be executed atomically in order to remain thread-safe.  Read-modify-write and Check-then-act must always be atomic to be thread-safe  Atomicity is relative to operation that are executed on shared state  The java.util.concurrent.atomic package contains atomic variable classes for effecting atomic state transitions 13Riccardo Cardin public class SafeSequence { private AtomicInteger value; public int getNext() { return value.incrementAndGet(); // Atomic read-modify-write } }
  • 14.
    Programmazione concorrente edistribuita RACE CONDITIONS 14Riccardo Cardin
  • 15.
    Programmazione concorrente edistribuita SHARING STATE  Writing correct concurrent programs is primarily about managing access to shared, mutable state  It’s all about memory visibility  We want to ensure that when a thread modifies the state of an object, other threads can actually see those changes  If shared state is represented by more than one variable, atomic classes are not useful 15Riccardo Cardin public class UnsafeCachedSequence { private AtomicInteger lastValue; private AtomicInteger value; public int getNext() { // Invariant of the class is not satisfied anymore lastValue.set(value.get()); return value.incrementAndGet(); } }
  • 16.
    Programmazione concorrente edistribuita LOCKING  To preserve state consistency, update related state variable in a single atomic operation  Java has a built-in locking mechanism for enforcing atomicity: synchronized block  But, it is easier to understand the synchronized keyword after having seen locks in isolation... 16Riccardo Cardin synchronized (lock) { // Access or modify shared state guarded by lock } Object that will serve as lock Block code to be guarded by the lock
  • 17.
    Programmazione concorrente edistribuita LOCKING  Reentrant locking  Use a Lock to protect a code block  The construct guarantees that only one thread at time can enter the critical section  Always release the lock in a finally block to prevents deadlocks  The class ReentranctLock implements basic functionalities of a lock 17Riccardo Cardin myLock.lock(); // a ReentrantLock object try { // Operation in this block are executed atomically } finally { // make sure the lock is unlocked even if an // exceptions thrown myLock.unlock(); }
  • 18.
    Programmazione concorrente edistribuita LOCKING  Reentrant locking  The lock acts as mutual exclusion locks 18Riccardo Cardin public class SafeCachedSequence { private Lock lock = new ReentrantLock(); private int lastValue; private int value; public int getNext() { // Invariant of the class is now satisfied lock.lock(); try { lastValue = value; value = value + 1; int result = value; } finally { lock.unlock(); } return result; } } Now the invariant of the class is guaranteed by the lock: value and lastValue will always be updated in a consistent way
  • 19.
    Programmazione concorrente edistribuita LOCKING  Reentrant locking 19Riccardo Cardin Thread 1 Thread 2 getNext getNext Unsynchronized Thread 1 Thread 2 getNext getNext Synchronized
  • 20.
    Programmazione concorrente edistribuita LOCKING  Reentrant locking  Different threads have to synchronize using the same instance of the lock  It is called reetrant because a thread can repeatedly acquire a lock that it already owns  The lock has a hold count that keeps track of the nested calls to the lock method  Prevents deadlocks wrt the subclass mechanism  Every object in Java (since 1.0) has an intrinsic lock  The synchronized keyword on a method protects the access to that method using this reference as lock object  All method’s code is guarded 20Riccardo Cardin
  • 21.
    Programmazione concorrente edistribuita LOCKING  Intrinsic locking  To call the method, a thread must acquire the intrinsic object lock  Static synchronized methods use the Class object as lock. 21Riccardo Cardin public synchronized void method() { // method body } // ...is equivalent to public void method() { this.intrinsicLock.lock(); try { // method body } finally { this.intrinsicLock.unlock(); } }
  • 22.
    Programmazione concorrente edistribuita LOCKING  Intrinsic locking suffers of performance issues  Use synchronization by Lock object  Or synchronized block  It uses the reference to an object that will serve as lock 22Riccardo Cardin public class SafeCachedSequence { private Object lock = new Object(); private int lastValue; private int value; public int getNext() { synchronized (lock) { lastValue = value; value = value + 1; int result = value; } return result; } }
  • 23.
    Programmazione concorrente edistribuita LOCKING 23Riccardo Cardin
  • 24.
    Programmazione concorrente edistribuita LOCKING PITFALLS  All the accesses to a mutable shared variable must be performed with the same lock held  Not only compound actions  Not all data needs to be guarded by a lock  Only mutable data  All the variables involved in the same invariant must be guarded by the same lock  It is not sufficient to use intrinsic lock on every method 24Riccardo Cardin // Not thread-safe if (!vector.contains(element)) vector.add(element);
  • 25.
    Programmazione concorrente edistribuita LOCKING PITFALLS  Poor concurrency  Limits by the availability of processing resources, not by the structure of application itself  CPU intensive and I/O operations must be outside synchronized blocks  Acquiring and releasing a lock has some overhead  Not break down synchronized blocks too far 25Riccardo Cardin There is frequently a tension between simplicity and performance. When implementing a synchronization policy, resist the temptation to prematurely sacrifice simplicity (potentially compromising safety) for the sake of performance. -- Brian Goetz
  • 26.
    Programmazione concorrente edistribuita EXAMPLES 26Riccardo Cardin https://github.com/rcardin/pcd-snippets
  • 27.
    Programmazione concorrente edistribuita REFERENCES  Chap. 1 «Introduction», Java Concurrency in Practice, Brian Goetz, 2006, Addison-Wesley Professional  Chap. 2 «Thread Safety», Java Concurrency in Practice, Brian Goetz, 2006, Addison-Wesley Professional  Chap. 3 «Sharing Objects», Java Concurrency in Practice, Brian Goetz, 2006, Addison-Wesley Professional  Dining philosophers problem https://en.wikipedia.org/wiki/Dining_philosophers_problem  Chap. 14 «Multithreading», Core Java Volume I - Fundamentals, Cay Horstmann, Gary Cornell, 2012, Prentice Hall  Intrinsic Locks and Synchronization https://docs.oracle.com/javase/tutorial/essential/concurrency/lock sync.html 27Riccardo Cardin