Effective Java: Concurrency Spring 2013

advertisement

Effective Java:

Concurrency

Spring 2013

Agenda

Material From Joshua Bloch

Effective Java: Programming Language Guide

Cover Items 66-73

“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++;}

// Also note anonymous class Concurrency in Java

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

// Issue is communication, not mutual exclusion!

Concurrency in Java

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

Example: StringBuffer vs. StringBuilder

Concurrency in Java

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)

Runnable and Callable

Mechanism for execution

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:

Instances of class appear constant

Example: String

Unconditionally thread-safe

Instances of class are mutable, but is internally synchronized

Example: ConcurrentHashMap

Conditionally thread-safe

Some methods require external synchronization

Example: Collections.synchronized wrappers

Not thread-safe

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

Download