Transcript Bloch10

Effective Java:
Concurrency
Last Updated: Fall 2011
Agenda

Material From Joshua Bloch


Cover Items 66-73


Effective Java: Programming Language Guide
“Concurrency” Chapter
Bottom Line:

Primitive Java concurrency is complex
Concurrency in Java
Item 66: Synchronize Access
to Shared Mutable Data


Method synchronization yields atomic
transitions:

public synchronized boolean doStuff() {…}

Fairly well understood…
Method synchronization also ensures that
other threads “see” earlier threads


Not synchronizing on shared “atomic” data
produces wildly counterintuitive results
Not well understood
Concurrency in Java
Item 66: Unsafe Example
// Broken! How long do you expect this program to run?
public class StopThread {
private static boolean stopRequested;
public static void main (String[] args)
throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
public void run() {
// May run forever!
int i=o; while (! stopRequested) i++; // See below
}});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
} }
// Hoisting transform:
// while (!loopTest) {i++;}  if (!loopTest) while(true) {i++;}
Concurrency in Java
// Also note anonymous class
Item 66: Fixing the Example
// As before, but with synchronized calls
public class StopThread {
private static boolean stopReq;
public static synchronized void setStop() {stopReq = true;}
public static synchronized void getStop() {return stopReq;}
public static void main (String[] args)
throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
public void run() {
// Now “sees” main thread
int i=o; while (! getStop() ) i++;
}});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
setStop();
} }
// Note that both setStop() and getStop() are synchronized
Concurrency in Java
// Issue is communication, not mutual exclusion!
Item 66: A volatile Fix for
the Example
// A fix with volatile
public class StopThread {
// Pretty subtle stuff, using the volatile keyword
private static volatile boolean stopRequested;
public static void main (String[] args)
throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
public void run() {
int i=o; while (! stopRequested) i++;
}});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
Concurrency in Java
Item 66: volatile Does Not
Guarantee Mutual Exclusion
// Broken! Requires Synchronization!
private static volatile int nextSerialNumber = 0;
public static int generateSerialNumber() {
return nextSerialNumber++;
}

Problem is that the “++” operator is not atomic
// Even better! (See Item 47)
private static final AtomicLong nextSerial = new AtomicLong();
public static long generateSerialNumber() {
return nextSerial.getAndIncrement();
}
Concurrency in Java
Item 66: Advice on Sharing
Data Between Threads


Share Immutable Data!
Confine mutable data to a single Thread


May modify, then share (no further changes)
 Called “Effectively Immutable”
 Allows for “Safe Publication”
Mechanisms for safe publication





In static field at class initialization
volatile field
final field
field accessed with locking (ie synchronization)
Store in concurrent collection (Item 69)
Concurrency in Java
Item 67: Avoid Excessive
Synchronization
// Broken! Invokes alien method from sychronized block
public interface SetOb<E> { void added(ObservableSet<E> set, E el);}
public class ObservableSet<E> extends ForwardingSet<E> { // Bloch 16
public ObservableSet(Set<E> set) { super(set); }
private final List<SetOb<E>> obs = new ArrayList<SetOb<E>>();
public void addObserver (SetObs<E> ob ) {
synchronized (obs) { obs.add(ob); } }
public boolean removeObserver (SetOb<E> ob ) {
synchronized (obs) { return obs.remove(ob); } }
private void notifyElementAdded (E el) {
synchronized(obs) { for (SetOb<E> ob:obs) // Exceptions?
ob.added(this, el);}
@Override public boolean add(E el) { // from Set interface
boolean added = super.add(el);
if (added) notifyElementAdded (el);
return added;
Concurrency in Java
}}
More Item 67: What’s the
Problem?
public static void main (String[] args) {
ObservableSet<Integer> set = new ObservableSet<Integer>
(new HashSet<Integer>);
set.addObserver (new SetOb<Integer>() {
public void added (ObservableSet<Integer> s, Integer e) {
System.out.println(e);
if (e.equals(23)) s.removeObserver(this); // Oops! CME
// See Bloch for a variant that deadlocks instead of CME
}
});
for (int i=0; i < 100; i++)
set.add(i);
}
Concurrency in Java
More Item 67: Turning the
Alien Call into an Open Call
// Alien method moved outside of synchronized block – open call
private void notifyElementAdded(E el) {
List<SetOb<E>> snapshot = null;
synchronized (observers) {
snapshot = new ArrayList<SetOb<E>>(obs);
}
for (SetObserver<E> observer : snapshot)
observer.added(this, el) // No more CME
}}



Open Calls increase concurrency and prevent failures
Rule: Do as little work inside synch block as possible
When designing a new class:


Do NOT internally synchronize absent strong motivation
Concurrency in Java
Example: StringBuffer vs. StringBuilder
Item 67: Alternate Fix Using
CopyOnWriteArray
public interface SetOb<E> { void added(ObservableSet<E> set, E el);}
public class ObservableSet<E> extends ForwardingSet<E> { // Bloch 16
public ObservableSet(Set<E> set) { super(set); }
private final List<SetOb<E>> obs = new
CopyOnWriteArrayList<SetOb<E>>();
public void addObserver (SetObs<E> ob ) {
synchronized (obs) { obs.add(ob); } }
public boolean removeObserver (SetOb<E> ob ) {
synchronized (obs) { return obs.remove(ob); } }
private void notifyElementAdded (E el) {
{for (SetOb<E> ob:obs) // Iterate on copy – No Synch!
ob.added(this, el);}
@Override public boolean add(E el) { // from Set interface
boolean added = super.add(el);
if (added) notifyElementAdded (el);
return added;
Concurrency in Java
}}
Item 68: Prefer Executors and
Tasks to Threads

Old key abstraction: Thread



Unit of work and
Mechanism for execution
New key abstractions:

Task (Unit of work)


Mechanism for execution



Runnable and Callable
Executor Service
Start tasks, wait on particular tasks, etc.
See Bloch for references
Concurrency in Java
Item 69: Prefer Concurrency
Utilities to wait and notify


wait() and notify() are complex
Java concurrency facilities much better


Legacy code still requires understanding low level
primitives
Three mechanisms


Executor Framework (Item 68)
Concurrent collections



Internally synchronized versions of Collection classes
Extensions for blocking, Example: BlockingQueue
Synchronizers

Objects that allow Threads to wait for one another
Concurrency in Java
More Item 69: Timing Example
// Simple framework for timing concurrent execution
public static long time (Executor executor, int concurrency, final Runnable
action) throws InterrruptedExecution {
final CountDownLatch ready = new CountDownLatch(concurrency);
final CountDownLatch start = new CountDownLatch(1);
final CountDownLatch done = new CountDownLatch(concurrency);
for (int i=0; i< concurrency; i++) {
executor.execute (new Runnable() {
public void run() { ready.countDown(); // Tell Timer we’re ready
try { start.await(); action.run(); // Wait till peers are ready
} catch (...){ ...}
} finally { done.countDown(); }} // Tell Timer we’re done
});}
ready.await();
// Wait for all workers to be ready
long startNanos = System.nanoTime();
start.countDown();
// And they’re off!
done.await()
// Wait for all workers to finish
return System.nanoTime() – startNanos;
}
Concurrency in Java
Item 70: Document Thread
Safety

Levels of Thread safety

Immutable:



Unconditionally thread-safe




Some methods require external synchronization
Example: Collections.synchronized wrappers
Not thread-safe



Instances of class are mutable, but is internally synchronized
Example: ConcurrentHashMap
Conditionally thread-safe


Instances of class appear constant
Example: String
Client responsible for synchronization
Examples: Collection classes
Thread hostile: Not to be emulated!
Concurrency in Java
More Item 70: Example
//Use Conditionally Thread-Safe Collections.synchronized wrapper
Map<K,V> m = Collections.synchronizedMap(new HashMap(K,V)());
...
Set<K> s = m.keySet(); // View needn’t be in synchronized block
...
synchronized (m) {
// Synchronizing on m, not s!
for (K key : s )
key.f();
// call f() on each key
// Documentation in Collections.synchronizedMap:
// “It is imperative that the user manually synchronize on the
// returned map when iterating over any of the collection views.”
Note that clients can (accidentally or intentionally) mount
denial-of-service attacks on other users of m by synchronizing
on m and then holding the lock. Private lock idiom thwarts this.
Concurrency in Java
Item 71: Use Lazy
Initialization Judiciously

Under most circumstances, normal
initialization is preferred
// Normal initialization of an instance field
private final FieldType field = computeFieldValue();
// Lazy initialization of instance field – synchronized accessor
private FieldType field;
synchronized FieldType getField() {
if (field == null)
field = computeFieldValue();
return field;
}
Concurrency in Java
More Item 71: Double Check
Lazy Initialization
// Double-check idiom for lazy initialization of instance fields
private volatile FieldType field; // volatile key – see Item 66
FieldType getField() {
FieldType result = field;
if (result == null) { // check with no locking
synchronized (this) {
result = field;
if (result == null) // Second check with a lock
field = result = computeFieldValue();
}
}
return result;
}
Concurrency in Java
Item 72: Don’t Depend on the
Thread Scheduler


Any program that relies on the thread
scheduler is likely to be unportable
Threads should not busy-wait


Use concurrency facilities instead (Item 69)
Don’t “Fix” slow code with
Thread.yield calls


Restructure instead
Avoid Thread priorities
Concurrency in Java
Item 73: Avoid Thread Groups

Thread groups originally envisioned as a
mechanism for isolating Applets for
security purposes

Unfortunately, doesn’t really work
Concurrency in Java