Sebastian Graf & Oliver Haase
Design Patterns & Concurrency
1
Thursday, April 28, 2011
Content today
1.Deadlocks
2.Explicit and Intrinsic Locks 3.Happens-Before in the JVM 4.Publication
5.Open Part
But before…
3
public class MutableLong private long value;
public long get() { return value; }
public void set(long value) { this.value = value; } }
public class MutableInt private int value;
public int get() { return value; }
public void set(int value) { this.value = value; } }
Thursday, April 28, 2011
Recap
‣ Visibility
‣ Publishing & Escaping
‣ Locking
‣ Cancelation of Threads
Deadlock
5
public class LeftRightDeadlock {
private final Object left = new Object();
private final Object right = new Object();
public void leftRight() { synchronized (left) {
synchronized (right) { doSomething();
} }
}
public void rightLeft() { synchronized (right) {
synchronized (left) { doSomethingElse();
} }
} }
Thursday, April 28, 2011
Deadlock
A program will be free of deadlocks if all locks are
acquired in one defined fixed global order.
Deadlock free?
7
public void work(final Object source, final Object target) {
synchronized (source) {
synchronized (target) {
moveStuff(source, target);
}
}
}
/** doing something */
protected abstract void moveStuff(final Object source,
final Object target);
Thursday, April 28, 2011
Deadlock secure
private static final Object tieLock = new Object();
public void work(final Object source, final Object target) { int fromHash = System.identityHashCode(source);
int toHash = System.identityHashCode(target);
if (fromHash < toHash) {
synchronized (source) {
synchronized (target) {
moveStuff(source, target);
} }
} else if (fromHash > toHash) { synchronized (target) {
synchronized (source) {
moveStuff(source, target);
} }
} else {
synchronized (tieLock) {
synchronized (source) {
synchronized (target) {
moveStuff(source, target);
} }
} }
protected abstract moveStuff(final Object source, final Object target);
}
Resource-bound deadlocks
9
public class ThreadDeadlock {
ExecutorService exec = Executors.newSingleThreadExecutor();
public class RenderPageTask implements Callable<String> { public String call() throws Exception {
Future<String> header, footer;
header = exec.submit(new LoadFileTask("header.html"));
footer = exec.submit(new LoadFileTask("footer.html"));
String page = renderBody();
// Will deadlock -- task waiting for result of subtask return header.get() + page + footer.get();
} }
}
Thursday, April 28, 2011
What to do if deadlock happens?
‣ …
When a lock is held…
‣ …check if it is really necessary.
‣ …refer to a global fixed order (and document it).
‣ …do not call alien methods.
‣ …use open calls.
‣ …cancel it after a given amount of time (tryLock)
11
Thursday, April 28, 2011
Explicit & Intrinsic Locking
public interface Lock { void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException;
void unlock();
...
}
TryLock
13
public boolean work(final Object source, final Object target, long timeout) { long fixedDelay = getFixedDelayComponentNanos(timeout, unit);
long randMod = getRandomDelayModulusNanos(timeout, unit);
long stopTime = System.nanoTime() + unit.toNanos(timeout);
while (true) {
if (source.lock.tryLock()) { try {
if (target.lock.tryLock()) { try {
moveStuff(source,target);
return true;
} finally {
target.lock.unlock();
} }
} finally {
source.lock.unlock();
} }
if (System.nanoTime() < stopTime) return false;
NANOSECONDS.sleep(fixedDelay + rnd.nextLong() % randMod);
} }
protected abstract moveStuff(final Object source, final Object target);
}
Thursday, April 28, 2011
Fairness of locks
“When set true, under contention, locks favor granting access to the longest-waiting
thread. Otherwise this lock does not guarantee any particular access order. “
Always Mutual Exclusion ?
15 public class ReadWriteMap<K,V> { private final Map<K,V> map;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock r = lock.readLock();
private final Lock w = lock.writeLock();
public ReadWriteMap(Map<K,V> map) { this.map = map;
}
public V put(K key, V value) { w.lock();
try {
return map.put(key, value);
} finally {
w.unlock();
} }
// Do the same for remove(), putAll(), clear() public V get(Object key) {
r.lock();
try {
return map.get(key);
} finally {
r.unlock();
} }
// Do the same for other read-only Map methods }
Thursday, April 28, 2011
Results
Multithreaded on JVM Level
17
public class PossibleReordering { static int x = 0, y = 0;
static int a = 0, b = 0;
public static void main(String[] args) throws InterruptedException {
Thread one = new Thread(new Runnable() { public void run() {
a = 1;
x = b;
} });
Thread other = new Thread(new Runnable() { public void run() {
b = 1;
y = a;
} });
one.start(); other.start();
one.join(); other.join();
System.out.println("( "+ x + "," + y + ")");
} }
Boils down to the “happens-before” rule.
Thursday, April 28, 2011
Reordering
Effects to publication
19
@NotThreadSafe
public class Resource {
private static Resource resource;
private Resource(){
}
public static Resource getInstance() { if (resource == null)
resource = new Resource(); // unsafe publication return resource;
} }
Thursday, April 28, 2011
Safer
@ThreadSafe
public class SafeLazyInitialization { private static Resource resource;
public synchronized static Resource getInstance() { if (resource == null)
resource = new Resource();
return resource;
} }
@ThreadSafe
public class EagerInitialization {
private static Resource resource = new Resource();
public static Resource getResource() { return resource; } }
Double Checked Locking
21
public class DoubleCheckedLocking { private static Resource resource;
public static Resource getInstance() { if (resource == null) {
synchronized (DoubleCheckedLocking.class) { if (resource == null)
resource = new Resource();
} }
return resource;
} }
Thursday, April 28, 2011
Holder Class & Enums
@ThreadSafe
public class ResourceFactory {
private static class ResourceHolder {
public static Resource resource = new Resource();
}
public static Resource getResource() { return ResourceHolder.resource ; }
}
@ThreadSafe
public enum EnumInitialization { SINGLETON;
final Resource resource = new Resource();
}
Discussion + Feedback
‣ What was repetition?
‣ What will you use in the future?
‣ What is missing, what can be simplified?
‣ …
23
Thursday, April 28, 2011