Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Java- Concurrent programming - Synchronization (part 1)

This presentation introduces the concept of synchronization beatween threads, as implemented in the Java platform. It is the first part of a series of slides dedicated to thread synchronization. This slides introduces the following concepts:

- Thread safety
- Types of race conditions
- Locking (reentrant locks, intrinsic locks, synchronized blocks)
- Locking pitfalls

The presentation is took from the Java course I run in the bachelor-level informatics curriculum at the University of Padova.

  • Login to see the comments

Java- Concurrent programming - Synchronization (part 1)

  1. 1. 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
  2. 2. Programmazione concorrente e distribuita SUMMARY  Introduction  Thread-safety  Race conditions  Locking  Locking pitfalls 2Riccardo Cardin
  3. 3. 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
  4. 4. 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
  5. 5. 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.
  6. 6. Programmazione concorrente e distribuita INTRODUCTION 6Riccardo Cardin
  7. 7. 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
  8. 8. 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
  9. 9. Programmazione concorrente e distribuita THREAD SAFETY 9Riccardo Cardin
  10. 10. 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
  11. 11. 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
  12. 12. 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
  13. 13. 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 } }
  14. 14. Programmazione concorrente e distribuita RACE CONDITIONS 14Riccardo Cardin
  15. 15. 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(); } }
  16. 16. 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
  17. 17. 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(); }
  18. 18. 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
  19. 19. Programmazione concorrente e distribuita LOCKING  Reentrant locking 19Riccardo Cardin Thread 1 Thread 2 getNext getNext Unsynchronized Thread 1 Thread 2 getNext getNext Synchronized
  20. 20. 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
  21. 21. 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(); } }
  22. 22. 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; } }
  23. 23. Programmazione concorrente e distribuita LOCKING 23Riccardo Cardin
  24. 24. 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);
  25. 25. 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
  26. 26. Programmazione concorrente e distribuita EXAMPLES 26Riccardo Cardin https://github.com/rcardin/pcd-snippets
  27. 27. 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

×