Java Con Currency Framework
Java Con Currency Framework
http://www.oopsla.org/oopsla2007/index.php?page=sub/&id=69
David Holmes
Senior Java Technologist Sun Microsystems Australia
[email protected]
Tim Peierls
BoxPop.biz [email protected]
Brian Goetz
Sun Microsystem Inc. [email protected]
See also
Concurrent Programming in Java, by Doug Lea, Addison-Wesley (CPJ)
4
The code executed by the main thread can create other threads
Either explicitly; or Implicitly via libraries:
AWT/Swing, Applets Servlets, web services RMI image loading 5
Runnable defines the abstraction of work Thread defines the abstraction of a worker
6
void start()
Creates a new thread of control to execute the run() method of the Thread object Can only be invoked once per Thread object
void join()
Waits for a thread to terminate
synchronized methods
public synchronized void op1(){ // execute op1 while holding this lock }
Changing a condition:
synchronized (obj) { // obj protects the mutable state condition = true; obj.notifyAll(); // or obj.notify() }
java.util.concurrent
General purpose toolkit for developing concurrent applications
No more reinventing the wheel!
10
Overview of j.u.c
Executors Executor ExecutorService ScheduledExecutorService Callable Future ScheduledFuture Delayed CompletionService ThreadPoolExecutor ScheduledThreadPoolExecutor AbstractExecutorService Executors FutureTask ExecutorCompletionService
11
Concurrent Collections:
Queues, blocking queues, concurrent hash map, Data structures designed for concurrent environments
Atomic variables
The key to writing lock-free algorithms
12
Cancellation and shutdown support Usually created via Executors factory class
Configures flexible ThreadPoolExecutor Customize shutdown methods, before/after hooks, saturation policies, queuing
13
Creating Executors
newFixedThreadPool(int N)
A fixed pool of N, working from an unbounded queue
newCachedThreadPool
A variable size pool that grows as needed and shrinks when idle
newScheduledThreadPool(int N)
Pool for executing tasks after a given delay, or periodically
14
Thread Pools
ThreadPoolExecutor
Keep-alive time
Threads above the core size terminate if idle for more than the keep-alive time In JDK 6 core threads can also terminate if idle
16
17
Futures
Encapsulates waiting for the result of an asynchronous computation launched in another thread
The callback is encapsulated by the Future object
Usage pattern
Client initiates asynchronous computation via oneway message Client receives a handle to the result: a Future Client performs additional tasks prior to using result Client requests result from Future, blocking if necessary until result is available Client uses result
Future<V> Interface
V get()
Retrieves the result held in this Future object, blocking if necessary until the result is available Timed version throws TimeoutException If cancelled then CancelledException thrown If computation fails throws ExecutionException
boolean isDone()
Queries if the computation has completedwhether successful, cancelled or threw an exception
boolean isCancelled()
Returns true if the computation was cancelled before it completed
19
Asynchronous rendering in a graphics application interface Pic { byte[] getImage(); } interface Renderer { Pic render(byte[] raw); } class App { // sample usage void app(final byte[] raw) throws ... { final Renderer r = ; FutureTask<Pic> p = new FutureTask<Pic>( new Callable<Pic>() { Pic call() { return r.render(raw); } }); new Thread(p).start(); doSomethingElse(); display(p.get()); // wait if not yet ready } // ... }
20
Concurrent Collections:
Queues, blocking queues, concurrent hash map, Data structures designed for concurrent environments
Atomic variables
The key to writing lock-free algorithms
21
Concurrent Collections
22
Concurrent Collections
23
Iteration Semantics
24
25
ConcurrentMap
Atomic get-and-maybe-set methods for maps interface ConcurrentMap<K,V> extends Map<K,V> { V putIfAbsent(K key, V value); V replace(K key, V value); boolean replace(K key, V oldValue, V newValue); boolean remove(K key, V value); }
26
Concurrent Collections:
Queues, blocking queues, concurrent hash map, Data structures designed for concurrent environments
Atomic variables
The key to writing lock-free algorithms
27
Locks
28
Lock / ReentrantLock
interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }
Additional flexibility
Interruptible, try-lock, not block-structured, multiple conditions Advanced uses: e.g. Hand-over-hand or chained locking
29
30
Concurrent Collections:
Queues, blocking queues, concurrent hash map, Data structures designed for concurrent environments
Atomic variables
The key to writing lock-free algorithms
31
Synchronizers
32
CountDownLatch
countDown: decrement the counter if > 0 Query: getCount() Very simple but widely useful:
Replaces error-prone constructions ensuring that a group of threads all wait for a common signal 33
Semaphores
fair variant hands out permits in FIFO order Supports balking and timed versions of acquire Applications:
Resource controllers Designs that otherwise encounter missed signals
Semaphores remember how often they were signalled 34
Tracking free space, or available items, can be done using a Semaphore Demonstrates composition of data structures with library synchronizers
Much, much easier than modifying implementation of concurrent list directly
35
36
Concurrent Collections:
Queues, blocking queues, concurrent hash map, Data structures designed for concurrent environments
Atomic variables
The key to writing lock-free algorithms
37
Atomic Variables
Holder classes for scalars, references and fields
java.util.concurrent.atomic
AtomicInteger Example
39
Tools:
ConcurrentHashMap FutureTask
40
Generic computation
interface Computable<A, V> { V compute(A arg) throws Exception; }
Representative example
class ComplexFunction implements Computable<String, BigInteger> { public BigInteger compute(String arg) { // after deep thought... return new BigInteger("2"); } }
41
Memoizer: Usage
Current use of ComplexFunction requires local caching of result (or expensive re-compute) Computable<String, BigInteger> f = new ComplexFunction(); BigInteger result = f.compute("1+1"); // cache result for future use
Memoizer encapsulates its own caching Computable<String, BigInteger> f = new ComplexFunction(); f = new Memoizer<String, BigInteger>(f); BigInteger result = f.compute("1+1"); // call f.compute whenever we need to
42
Synchronized Memoizer
Safe but not concurrent class SyncMemoizer<A,V> implements Computable<A,V> { final Map<A, V> cache = new HashMap<A, V>(); final Computable<A, V> func; SyncMemoizer(Computable<A, V> func) { this.func = func; } public synchronized V compute(A arg)throws Exception{ if (!cache.containsKey(arg)) cache.put(arg, func.compute(arg)); return cache.get(arg); } }
43
Safe, concurrent (no sync) but computes may overlap class NonAtomicMemoizer<A,V> implements Computable<A,V> { final Map<A, V> cache = new ConcurrentHashMap<A, V>(); final Computable<A, V> func; NonAtomicMemoizer(Computable<A, V> func) { this.func = func; } public V compute(A arg) throws Exception { if (!cache.containsKey(arg)) cache.put(arg, func.compute(arg)); return cache.get(arg); }
44
Safe, concurrent and exactly one compute per argument class ConcurrentMemoizer<A, V> implements Computable<A, V> { final ConcurrentMap<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>(); final Computable<A, V> func; ConcurrentMemoizer(Computable<A, V> func) { this.func = func; } ...
45
46
Tools:
ReentrantLock
Tools:
Semaphore
47
Hand-over-hand Locking:
Lock n1, lock n2, unlock n1, lock n3, unlock n2, lock n4, Order in which threads acquire the first lock is maintained
No overtaking once traversal starts
Locking considerations
What needs to be unlocked in the normal case? What needs to be unlocked if an exception occurs?
Will the list still be in a consistent state? Note: cant protect against asynchronous exceptions
Simple in this case: only one lock held, only one failure mode
Again exception handling is easy to do but harder to reason about! Note: NullPointerException and IllegalMonitorStateException only possible if list code is broken 51
53