Title: Effective Java: Concurrency
1Effective JavaConcurrency
2Agenda
- Material From Joshua Bloch
- Effective Java Programming Language Guide
- Cover Items 66-73
- Concurrency Chapter
- Bottom Line
- Primitive Java concurrency is complex
3Item 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
4Item 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 io while (! stopRequested) i
// hoisting? - )
- backgroundThread.start()
- TimeUnit.SECONDS.sleep(1)
- stopRequested true
-
5Item 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 io while (! getStop() ) i
- )
- backgroundThread.start()
- TimeUnit.SECONDS.sleep(1)
- setStop()
-
6Item 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 io while (! stopRequested) i
- )
- backgroundThread.start()
- TimeUnit.SECONDS.sleep(1)
- stopRequested true
-
7Item 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()
-
8Item 66 Advice on Sharing Data Between Threads
- 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)
9Item 67 Avoid Excessive Synchronization
- // Broken! Invokes alien method from sychronized
block - public interface SetObltEgt void
added(ObservableSetltEgt set, E el) - public class ObservableSetltEgt extends
ForwardingSetltEgt // Bloch 16 - public ObservableSet(SetltEgt set) super(set)
- private final ListltSetObltEgtgt obs new
ArrayListltSetObltEgtgt() - public void addObserver (SetObsltEgt obs )
- synchronized (obs) obs.add(ob)
- public boolean removeObserver (SetObltEgt ob )
- synchronized (obs) return obs.remove(ob)
- private void notifyElementAdded (E el)
- synchronized(obs) for (SetObltEgt obobs) //
Exceptions? - ob.added(this, el)
- _at_Override public boolean add(E el) // from
Set interface - boolean added super.add(el)
- if (added) notifyElementAdded (el)
- return added
10More Item 67 Whats the Problem?
- public static void main (String args)
- ObservableSetltIntegergt set new
ObservableSetltIntegergt - (new HashSetltIntegergt)
- set.addObserver (new SetObltIntegergt()
- public void added (ObservableSetltIntegergt
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 i0 i lt 100 i)
- set.add(i)
11More Item 67 Turning the Alien Call into an Open
Call
- // Alien method moved outside of synchronized
block open call - private void notifyElementAdded(E el)
- ListltSetObltEgtgt snapshot null
- synchronized (observers)
- snapshot new ArrayListltSetObltEgtgt(obs)
-
- for (SetObserverltEgt 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
12Item 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
13Item 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
14More 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 i0 ilt concurrency i)
- executor.execute (new Runnable()
- public void run() ready.countDown()
// Tell Timer were ready - try start.await() action.run()
// Wait till peers are ready - catch (...) ...
- finally done.countDown() //
Tell Timer were done - )
- ready.await() // Wait for all workers
to be ready - long startNanos System.nanoTime()
- start.countDown() // And theyre off!
- done.await() // Wait for all workers
to finish - return System.nanoTime() startNanos
15Item 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!
16Item 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
17More 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
18Item 72 Dont 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)
- Dont Fix slow code with Thread.yield calls
- Restructure instead
- Avoid Thread priorities
19Item 73 Avoid Thread Groups
- Thread groups originally envisioned as a
mechanism for isolating Applets for security
purposes - Unfortunately, doesnt really work