More thread safety and Concurrent collections Nasser Giacaman CompSci 230: Software Design and Construction 1
Today's lecture More on Java's mutual exclusion tools Intrinsic locks Developing our own thread-safe classes Concurrent collections 2
Synchronized methods public class SynchronizedCounter { private int c = 0; public synchronized void increment() { c++; public synchronized void decrement() { c--; public synchronized int value() { return c; Given a particular instance of SynchronizedCounter, only 1 thread may be inside any synchronized method at a tme Any other threads wishing to call a synchronized method on the same instance must wait in turn Synchronized constructors don't make sense! Synchronized methods are rather coarse-grained, the entire method is serialised, even if some statements are not in a critical section (and could safely be executed concurrently by other threads) 3
Synchronized blocks public class SynchronizedCounter { private int c = 0; // Object intrinsiclock = new Object(); public void increment() { synchronized(this) { // start lock (using this object's intrinsic lock) c++; // end lock Similar to synchronized methods, but the synchronized regions are finer-grained now, allowing for more concurrency Every instance in Java has its own intrinsic lock When a synchronized method or synchronised(this) is called, that instance's intrinsic lock is used.. So by sharing the same lock, this still might be too coarse-grained for some situations 4
public class SynchronizedCounter { private int a = 0; private int b = 0; Object locka = new Object(); Object lockb = new Object(); public void inca() { synchronized(locka) { a++; public void incb() { synchronized(lockb) { b++; Synchronized blocks Provides better concurrency :-) But starts to complicate things :-( 5
Atomic variables public class AtomicCounter { private AtomicInteger c = new AtomicInteger(0); public void increment() { c.incrementandget(); public void decrement() { c.decrementandget(); public void set(int newvalue) { c.set(newvalue); public int value() { return c.get(); Have a look at the java.util.concurrent.atomic package Lots more methods inside AtomicInteger Lots more classes inside this package AtomicBoolean AtomicReference 6
Locks class LockedCounter { private final Lock lock = new ReentrantLock(); private int c = 0; public void increment() { lock.lock(); try { c++; finally { lock.unlock()... Have a look at the java.util.concurrent.locks package More flexibility and efficient than using synchronized blocks Optional fair policy trylock() getqueuelength()... 7
Deadlock! class UhOh { private final Lock locka = new ReentrantLock(); private final Lock lockb = new ReentrantLock(); private int a, b; public void doa() { locka.lock(); lockb.lock(); lockb.unlock(); locka.unlock(); public void dob() { lockb.lock(); locka.lock(); locka.unlock(); lockb.unlock();... 8
Concurrent collections By using the previous tools, you are able to produce thread-safe classes. This means that users of your class don't need to implement mutual exclusion on your class Good OOP practice! encapsulation! By extending this, classes that represent collections can also be thread-safe.. so multiple threads writing and reading into the collection safely! The collection itself is aware of all the threads and implements mutual exclusion whereever needed.. makes your life easier, you don't need to do any locking, etc! reassuring to make use of a well-tested class (code reuse!) See the java.util.concurrent package, lots of collections there 9
Concurrent collections Why can't we share an ArrayList between multiple threads? Recall the Counter example! What will be the size() of the collection after a couple of threads add something!?!?! That's why we can't share any mutable objects that are not thread-safe! Demo! A collection of Animals shared by multiple owners (i.e. multiple threads)! Iterator (not thread-safe) (e.g.) ArrayList (not thread-safe) Parallel Iterator (thread-safe) (e.g.) ConcurrentLinkedQueue, LinkedBlockingQueue, etc 10