Download Java Concurrency Utilities: Understanding Executors, Futures, and Concurrent Collections and more Exams Programming Languages in PDF only on Docsity!
Java Concurrency Utilities
Based on JavaOne talk given by
David Holmes & Brian Goetz
Overview
• Rationale and goals for JSR 166
– Java community process – concurrency utilities
• Executors – thread pools and scheduling
• Futures
• Concurrent Collections
• Locks, conditions and synchronizers
• Atomic variables
Why Concurrency Utilities
• Java’s built-in concurrency primitives -- wait(),
notify(), and synchronized – are:
• Hard to use correctly
• Easy to use incorrectly
• Too low level for many applications
• Can lead to poor performance if used incorrectly
• Leave out lots of useful concurrency constructs
Goals
• Provide efficient, correct & reusable concurrency
building blocks
• Enhance scalability, performance, readability,
maintainability, and thread-safety of concurrent
Java applications
Executor
• Standardizes asynchronous invocation
• Separates job submission from execution policy
- anExecutor.execute(aRunnable)
- not new Thread(aRunnable).start()
• Two code styles supported:
- Actions: Runnables
- Functions: Callables
- Also has lifecycle mgmt: cancellation, shutdown, etc.
• Executor usually created via Executors factory class
- Configures ThreadPoolExecutor
- Customizes shutdown methods, before/after hooks, saturation policies, queuing
Executor & ExecutorService
• ExecutorService adds lifecycle management to Executor
public interface Executor { void execute( Runnable command ); } public interface ExecutorService extends Executor { void shutdown() ; List shutdownNow(); boolean isShutdown(); boolean isTerminated(); boolean awaitTermination( long timeout , TimeUnit unit ); // other convenience methods for submitting tasks }
Creating Executors
• Executors factory methods
public class Executors {
static ExecutorService newSingleThreadedExecutor();
static ExecutorService newFixedThreadPool( int n);
static ExecutorService newCachedThreadPool( int n);
static ScheduledExecutorService newScheduledThreadPool( int n);
// additional versions & utility methods
(Not) Executor Example
• Thread per message Web Server (no limit on thread creation)
class WebServer { public static void main( String [] args) { ServerSocket socket = new ServerSocket ( 80 ); while ( true ) { final Socket connection = socket.accept(); Runnable r = new Runnable () { public void run () {handleRequest(connection);} }; new Thread (r).start(); } } }
Future Example
• See: FutureTaskStringReverser.java
Another Future Example
• Implementing a cache with Future
public class Cache<K, V> { Map<K, Future> map = new ConcurrentHashMap(); Executor executor = Executors.newFixedThreadPool(8); public V get (final K key) { Future f = map.get(key); // null if key not found if (f == null) { Callable c = new Callable() { public V call() {// compute value associated with key}}; f = new FutureTask(c); Future old = map.putIfAbsent(key, f); // return null if key not found. put(key,f) if (old == null) // otherwise return get(key) executor.execute(f); else f = old; } return f.get(); } }
ScheduledExecutorService
• For deferred and recurring tasks, can schedule
- Callable or Runnable to run once with a fixed delay after submission
- Schedule a Runnable to run periodically at a fixed rate
- Schedule a Runnable to run periodically with a fixed delay between executions
• Submission returns a ScheduledFutureTask handle which
can be used to cancel the task
• Like Timer , but supports pooling and is more robust
Concurrent Collections
• Pre-1.5 Java class libraries had few concurrent (vs
Synchronized) classes
– Synchronized collections: Hashtable, Vector, and
Collections.synchronizedXXX
- Often required locking during iteration
- Locking becomes is a source of contention
• Java 1.5 concurrent collections:
– Allow multiple operations to overlap
- Some differences in semantics
Producer-Consumer Examples
• See:
– ProducerConsumerPrimitive.java (wait/notify)
– ProducerConsumerConcUtil.java (BlockingQueue)
Concurrent Collections
• ConcurrentHashMap - Concurrent (scalable) alt. to
Hashtable or Collections.synchronizedMap
- Multiple reads can overlap each other
- Reads can overlap writes
- Retrieval operations reflect the results of the most recently completed update operations holding at onset of operation
- Up to 16 writes can overlap
- Iterators do not throw ConcurrentModificationException
• CopyOnWriteArrayList
- Optimized for case where iteration is much more frequent than insertion or removal. E.g., event listeners
Performance Comparison
• ConcurrentHashMap vs. Collections.synchronizedMap
• See HashMapPerfTest.java
• Note: incrementCount() is not safe
Locks and Lock Support
• High-level locking interface. Adds non-blocking lock
acquistion
interface Lock { void lock(); void lockInterruptibly() throws IE; boolean tryLock(); boolean tryLock( long time ,TimeUnit unit ) throws IE; void unlock(); Condition newCondition() throws UnsupportedOperationException; }
Read/write Locks
• ReadWriteLock defines a pair of locks; one for readers;
one for writers
interface ReadWriteLock { Lock readLock(); Lock writeLock(); }
• ReentrantReadWriteLock class
- Provides reentrant read and write locks
- Allows writer to acquire read lock
- Allows writer to downgrade to read lock
- Supports “fair” and “writer preference” acquisition
Read/Write Lock Example
class RWDictionaryRWL { private final Map < String , Data > m = new TreeMap < String , Data >(); private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock (); private final Lock r = rwl.readLock (); private final Lock w = rwl.writeLock (); public Data get( String key) { r.lock(); try { return m.get(key); } finally { r.unlock(); } } public Data put( String key, Data value) w.lock(); try { return m.put(key, value); } finally { w.unlock(); } } }
Read/Write Lock Example
• See
– RWDictionary .java & RWDictionaryRWL.java
Condition
• Condition lets you wait for a condition to hold
interface Condition { void await() throws IE ; boolean await( long time, TimeUnit unit ) throws IE ; long awaitNanos( long nanosTimeout) throws IE ; void awaitUninterruptibly() boolean awaitUntil( Date deadline) throws IE ; void signal(); void signalAll(); }
Condition Example (cont.)
public Object take() throws IE { lock.lock(); try { while (count == 0) notEmpty.await(); Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.unlock(); } } }
Condition Example (cont.)
• Previous example in BoundedBufferCond.java
• See also: BoundedBufferPrim.java
Synchronizers
• Utilities for coordinating access and control
• CountDownLatch – allows one or more threads to
wait for a set of threads to complete an action
• CyclicBarrier – allows a set of threads to wait until
they all reach a specified barrier point
• Semaphore – Dijkstra counting semaphore , managing
some number of permits
• Exchanger – allows two threads to rendezvous and
exchange data, such as exchanging an empty buffer
for a full one
CountDownLatch
• Latching variables are conditions that once set
never change.
• Often used to start several threads, but have them
wait for a signal before continuing
• See: CountDownLatchTest.java
Exchanger
• Synch. point where two threads exchange objects
• A bidirectional SynchronizedQueue
• Each thread presents some object on entry to the
exchange() method, and receives the object
presented by the other thread on return
• See ExchangerTest.java
Atomic Variables
• Holder classes for scalars, references and fields
• Supports atomic operations
- Compare-and-set (CAS)
- Get and set and arithmetic (where applicable)
• Ten main classes: { int, long, ref } X { value, field, array }
- E.g. AtomicInteger useful for counters, sequences, statistics
• Essential for writing efficient code on MPs
- Nonblocking data structures & optimistic algorithms
- Reduce overhead/contention updating “hot” fields
• JVM uses best construct available on platform
- CAS, load-linked/store-conditional, locks
Atomic Variables
• See: CounterTest.java