1
More thread safety
and Concurrent collections
Nasser Giacaman
CompSci 230: Software Design and Construction
Today's lecture
●
More on Java's mutual exclusion tools
– Intrinsic locks
●
Developing our own thread-safe classes
●
Concurrent collections
3
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)
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
5
Synchronized blocks
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++;
} }
● Provides better concurrency :-)
● But starts to complicate things :-(
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
– …
7
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()
– ...
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();
}
9
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
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