Java Concurrency: by Alex Miller
Java Concurrency: by Alex Miller
About Java Concurrency Concepts Protecting Shared Data Concurrent Collections Threads Threads Coordination and more...
Concepts
This section describes key Java Concurrency concepts that are used throughout this DZone Refcard.
www.dzone.com
Hot Tip
Data changed outside synchronization has NO specified semantics under the Java Memory Model! The JVM is free to reorder instructions and limit visibility in ways that are likely to be surprising to a developer.
Synchronized
Every object instance has a monitor that can be locked by one thread at a time. The synchronized keyword can be specified on a method or in block form to lock the monitor. Modifying a field while synchronized on an object guarantees that subsequent reads from any other thread synchronized on the same object will see the updated value. It is important to note that writes outside synchronization or synchronized on a different object than the read are not necessarily ever visible to other threads.
Monitor
Race condition
Data race
Safe publication
Final fields
DZone, Inc.
www.dzone.com
Java Concurrency
The synchronized keyword can be specified on a method or in block form on a particular object instance. If specified on a non-static method, the this reference is used as the instance. In a synchronized static method, the Class defining the method is used as the instance.
Lock
The java.util.concurrent.locks package has a standard Lock interface. The ReentrantLock implementation duplicates the functionality of the synchronized keyword but also provides additional functionality such as obtaining information about the state of the lock, non-blocking tryLock(), and interruptible locking. Example of using an explicit ReentrantLock instance:
public class Counter { private final Lock lock = new ReentrantLock(); private int value = 0; public int increment() { lock.lock(); try { return ++value; } finally { lock.unlock(); } } }
Hot Tip
Marking an array as volatile does not make entries in the array volatile! In this case volatile applies only to the array reference itself. Instead, use a class like AtomicIntegerArray to create an array with volatilelike entries.
Atomic classes
One shortcoming of volatile is that while it provides visibility guarantees, you cannot both check and update a volatile field in a single atomic call. The java.util.concurrent.atomic package contains a set of classes that support atomic compound actions on a single value in a lock-free manner similar to volatile.
public class Counter { private AtomicInteger value = new AtomicInteger(); public int next() { return value.incrementAndGet(); } }
ReadWriteLock
The java.util.concurrent.locks package also contains a ReadWriteLock interface (and ReentrantReadWriteLock implementation) which is defined by a pair of locks for reading and writing, typically allowing multiple concurrent readers but only one writer. Example of using an explicit ReentrantReadWriteLock to allow multiple concurrent readers:
public class Statistic { private final ReadWriteLock lock = new ReentrantReadWriteLock(); private int value; public void increment() { lock.writeLock().lock(); try { value++; } finally { lock.writeLock().unlock(); } } public int current() { lock.readLock().lock(); try { return value; } finally { lock.readLock().unlock(); } } }
The incrementAndGet method is just one example of a compound action available on the Atomic classes. Atomic classes are provided for booleans, integers, longs, and object references as well as arrays of integers, longs, and object references.
ThreadLocal
One way to contain data within a thread and make locking unnecessary is to use ThreadLocal storage. Conceptually a ThreadLocal acts as if there is a variable with its own version in every Thread. ThreadLocals are commonly used for stashing per-Thread values like the current transaction or other resources. Also, they are used to maintain per-thread counters, statistics, or ID generators.
public class TransactionManager { private static final ThreadLocal<Transaction> currentTransaction = new ThreadLocal<Transaction>() { @Override protected Transaction initialValue() { return new NullTransaction(); } }; public Transaction currentTransaction() { Transaction current = currentTransaction.get(); if(current.isNull()) { current = new TransactionImpl(); currentTransaction.put(current); } return current; } }
volatile
The volatile modifier can be used to mark a field and indicate that changes to that field must be seen by all subsequent reads by other threads, regardless of synchronization. Thus, volatile provides visibility just like synchronization but scoped only to each read or write of the field. Before Java SE 5, the implementation of volatile was inconsistent between JVM implementations and architectures and could not be relied upon. The Java Memory Model now explicitly defines volatiles behavior. An example of using volatile as a signaling flag:
public class Processor implements Runnable { private volatile boolean stop; public void stopProcessing() { stop = true;
Concurrent Collections
A key technique for properly protecting shared data is to encapsulate the synchronization mechanism with the class holding the data. This technique makes it impossible to improperly access the data as all usage must conform to the synchronization protocol. The java.util.concurrent package holds many data structures designed for concurrent use. Generally, the use of these data structures yields far better performance than using a synchronized wrapper around an unsynchronized collection.
|
DZone, Inc.
www.dzone.com
Java Concurrency
In these cases, BlockingQueue provides methods that either block forever or block for a specified time period, waiting for the condition to change due to the actions of another thread. Table 5 demonstrates the Queue and BlockingQueue methods in terms of key operations and the strategy for dealing with these special conditions.
Table 5: Queue and BlockingQueue methods
Method Queue Strategy Throw Exception Return special value Blocking Queue Block forever Block with timer Insert add offer put offer Remove remove poll take poll Examine element peek n/a n/a
CopyOnWriteArrayList
ConcurrentSkipListSet
Several Queue implementations are provided by the JDK and their relationships are discribed in Table 6.
Table 6: Queue Implementations
Method PriorityQueue Description PriorityQueue is the only non-concurrent queue implementation and can be used by a single thread to collect items and process them in a sorted order. An unbounded linked list queue implementation and the only concurrent implementation not supporting BlockingQueue. A bounded blocking queue backed by an array. An optionally bounded blocking queue backed by a linked list. This is probably the most commonly used Queue implementation. An unbounded blocking queue backed by a heap. Items are removed from the queue in an order based on the Comparator associated with the queue (instead of FIFO order). An unbounded blocking queue of elements, each with a delay value. Elements can only be removed when their delay has passed and are removed in the order of the oldest expired item. A 0-length queue where the producer and consumer block until the other arrives. When both threads arrive, the value is transferred directly from producer to consumer. Useful when transferring data between threads.
Concurrent maps
The java.util.concurrent package contains an extension to the Map interface called ConcurrentMap, which provides some extra methods described in Table 3. All of these methods perform a set of actions in the scope of a single atomic action. Performing this set of actions outside the map would introduce race conditions due to making multiple (non-atomic) calls on the map.
Table 3: ConcurrentMap methods
Method putIfAbsent(K key, V value) : V Description If the key is not in the map then put the key/ value pair, otherwise do nothing. Returns old value or null if not previously in the map. If the map contains key and it is mapped to value then remove the entry, otherwise do nothing. If the map contains key then replace with newValue, otherwise do nothing. If the map contains key and it is mapped to oldValue then replace with newValue, otherwise do nothing.
PriorityBlockingQueue
DelayQueue
remove(Object key, Object value) : boolean replace(K key, V value) : V replace(K key, V oldValue, V newValue) : boolean
SynchronousQueue
Deques
A double-ended queue or Deque (pronounced deck) was added in Java SE 6. Deques support not just adding from one end and removing from the other but adding and removing items from both ends. Similarly to BlockingQueue, there is a BlockingDeque interface that provides methods for blocking and timeout in the case of special conditions. Table 7 shows the Deque and BlockingDeque methods. Because Deque extends Queue and BlockingDeque extends BlockingQueue, all of those methods are also available for use.
Table 7: Deque and BlockingDeque methods
Interface Queue First or Last Head Strategy Throw exception Return special value Tail Throw exception Return special value Blocking Queue Head Block forever Block with timer Tail Block forever Block with timer Insert
addFirst offerFirst addLast offerLast putFirst offerFirst putLast offerLast
ConcurrentSkipListMap
Remove
removeFirst pollFirst removeLast pollLast takeFirst pollFirst takeLast pollLast
Examine
getFirst peekFirst getLast peekLast
Queues
Queues act as pipes between producers and consumers. Items are put in one end of the pipe and emerge from the other end of the pipe in the same first-in first-out (FIFO) order. The Queue interface was added to java.util in Java SE 5 and while it can be used in single-threaded scenarios, it is primarily used with multiple producers or one or more consumers, all writing and reading from the same queue. The BlockingQueue interface is in java.util.concurrent and extends Queue to provide additional choices of how to handle the scenario where a queue may be full (when a producer adds an item) or empty (when a consumer reads or removes an item).
DZone, Inc.
|
One special use case for a Deque is when add, remove, and examine operations all take place on only one end of the pipe. This special case is just a stack (first-in-last-out retrieval order). The Deque interface actually provides methods that use the terminology of a stack: push(), pop(), and peek(). These
www.dzone.com
Java Concurrency
methods map to addFirst(), removeFirst(), and peekFirst() methods in the Deque interface and allow you to use any Deque implementation as a stack. Table 8 describes the Deque and BlockingDeque implementations in the JDK. Note that Deque extends Queue and BlockingDeque extends BlockingQueue
Table 8: Deques
Class LinkedList Description This long-standing data structure has been retrofitted in Java SE 6 to support the Deque interface. You can now use the standard Deque methods to add or remove from either end of the list (many of these methods already existed) and also use it as a nonsynchronized stack in place of the fully synchronized Stack class. This implementation is not concurrent and supports unbounded queue length (it resizes dynamically as needed). The only concurrent deque implementation, this is a blocking optionally-bounded deque backed by a linked list.
making progress. Livelock occurs when threads spend all of their time negotiating access to a resource or detecting and avoiding deadlock such that no thread actually makes progress.
Thread Coordination
wait / notify
The wait / notify idiom is appropriate whenever one thread needs to signal to another that a condition has been met, especially as an alternative to sleeping in a loop and polling the condition. For example, one thread might wait for a queue to contain an item to process. Another thread can signal the waiting threads when an item is added to the queue. The canonical usage pattern for
wait
ArrayDeque LinkedBlockingDeque
Threads
In Java, the java.lang.Thread class is used to represent an application or JVM thread. Code is always being executed in the context of some Thread class (use Thread.currentThread() to obtain your own Thread).
public class Latch { private final Object lock = new Object(); private volatile boolean flag = false; public void waitTillChange() { synchronized(lock) { while(! flag) { try { lock.wait(); } catch(InterruptedException e) { } } } } public void change() { synchronized(lock) { flag = true; lock.notifyAll(); } } }
Thread Communication
The most obvious way to communicate between threads is for one thread to directly call a method on another Thread object. Table 9 shows methods on Thread that can be used for direct interaction across threads.
Table 9: Thread coordination methods
Thread Method start join interrupt Description Start a Thread instance and execute its run() method. Block until the other Thread exits Interrupt the other thread. If the thread is blocked in a method that responds to interrupts, an InterruptedException will be thrown in the other thread, otherwise the interrupt status is set. These methods are all deprecated and should not be used. They perform dangerous operations depending on the state of the thread in question. Instead, use interrupt() or a volatile flag to indicate to a thread what it should do.
Condition
In Java SE 5, a new java.util.concurrent.locks.Condition class was added. Condition implements the wait/notify semantics in an API but with several additional features such as the ability to create multiple Conditions per Lock, interruptible waiting, access to statistics, etc. Conditions are obtained from a Lock instance as follows:
public class LatchCondition { private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private volatile boolean flag = false; public void waitTillChange() { lock.lock(); try { while(! flag) { condition.await(); } } finally { lock.unlock(); } } public void change() { lock.lock(); try {
Deadlock
A deadlock occurs when there is more than one thread, each waiting for a resource held by another, such that a cycle of resources and acquiring threads is formed. The most obvious kind of resource is an object monitor but any resource that causes blocking (such as wait / notify) can qualify. Many recent JVMs can detect monitor deadlocks and will print deadlock information in thread dumps produced from a signal, jstack, or other thread dump tool. In addition to deadlock, some other threading situations are starvation and livelock. Starvation occurs when threads hold a lock for long periods such that some threads starve without
DZone, Inc.
|
www.dzone.com
Java Concurrency
Coordination classes
The java.util.concurrent package contains several classes pre-built for common forms of multi-thread communication. These coordination classes cover most common scenarios where wait/notify and Condition might be used and are strongly perferred for their safety and ease of use. CyclicBarrier The CyclicBarrier is initialized with a participant count. Participants call await() and block until the count is reached, at which point an optional barrier task is executed by the last arriving thread, and all threads are released. The barrier can be reused indefinitely. Used to coordinate the start and stop of groups of threads. CountDownLatch The CountDownLatch is initialized with a count. Threads may call await() to wait for the count to reach 0. Other threads (or same) may call countDown() to reduce count. Not reusable once the count has reached 0. Used to trigger an unknown set of threads once some number of actions has occurred. Semaphore A Semaphore manages a set of permits that can be checked out with acquire() which will block until one is available. Threads call release() to return the permit. A semaphore with one permit is equivalent to a mutual exclusion block. Exchanger An Exchanger waits for threads to meet at the exchange() method and swap values atomically. This is similar to using a SynchronousQueue but data values pass in both directions.
ExecutorService implementations
The primary implementation of ExecutorService is ThreadPoolExecutor. This implementation class provides a wide variety of configurable features: Thread pool specify core thread count (optionally prestarted), and max thread count Thread factory generate threads with custom characteristics such as a custom name Work queue specify the queue implementation, which must be blocking, but can be bounded or unbounded Rejected tasks specify the policy for tasks that cannot be accepted due to a full input queue or unavailable worker Lifecycle hooks overridden to extend to override key points in the lifecycle like before or after task execution Shutdown stop incoming tasks and wait for executing tasks to complete is an extension of that provides the ability to schedule tasks for completion rather than using FIFO semantics. For cases where java.util.Timer is not sophisticated enough, the ScheduledThreadPoolExecutor often provides sufficient flexibility.
ScheduledThreadPoolExecutor ThreadPoolExecutor
Task execution
Many concurrent Java programs need a pool of workers executing tasks from a queue. The java.util.concurrent package provides a solid foundation for this style of work management.
ExecutorService
The Executor and more expansive ExecutorService interfaces define the contract for a component that can execute tasks. Users of these interfaces can get a wide variety of implementation behaviors behind a common interface. The most generic Executor interface accepts jobs only in the form of Runnables: void execute(Runnable command) The ExecutorService extends Executor to add methods that take both Runnable and Callable task and collections of tasks: Future<?> submit(Runnable task) Future<T> submit(Callable<T> task) Future<T> submit(Runnable task, T result) List<Future<T> invokeAll(Collection<? extends
Callable<T>> tasks)
The Executors class contains many static methods (see Table 10) for creating prepackaged ExecutorService and ScheduledExecutorService instances that will cover a wide variety of common use cases.
Table 10: Executors factory methods
Method newSingleThreadExecutor newFixedThreadPool newCachedThreadPool newSingleThreadScheduledExecutor newScheduledThreadPool Description Returns an ExecutorService with exactly one thread. Returns an ExecutorService with a fixed number of threads. Returns an ExecutorService with a varying size thread pool. Returns a ScheduledExecutorService with a single thread. Returns a ScheduledExecutorService with a core set of threads.
www.dzone.com
Java Concurrency
The following example creates a fixed thread pool and submits a long-running task to it:
int processors = Runtime.getRuntime().availableProcessors(); ExecutorService executor = Executors. newFixedThreadPool(processors); Future<Integer> futureResult = executor.submit( new Callable<Integer>() { public Integer call() { // long running computation that returns an integer } }); Integer result = futureResult.get(); // block for result
produced by one of the Executor factory methods; often this will be simpler and more flexible.
CompletionService
Beyond the common pattern of a pool of workers and an input queue, it is common for each task to produce a result that must be accumulated for further processing. The CompletionService interface allows a user to submit Callable and Runnable tasks but also to take or poll for results from the results queue: Future<V> take() take if available Future<V> poll() block until available Future<V> poll(long timeout, TimeUnit unit) block
until timeout ends
In this example the call that submits the task to the executor will not block but return immediately. The last line will block on the get() call until the result is available. covers almost all situations where you would previously create Thread objects or thread pools. Any time your code is constructing a Thread directly, consider whether you could accomplish the same goal with an ExecutorService
ExecutorService
The ExecutorCompletionService is the standard implementation of CompletionService. It is constructed with an Executor that provides the input queue and worker thread pool.
Hot Tip
When sizing thread pools, it is often useful to base the size on the number of logical cores in the machine running the application. In Java, you can get that value by calling Runtime.getRuntime().availableProcessors(). The number of available processors may change during the lifetime of a JVM.
A BOUT t h e A u t h o r ers of the open-source Java clustering product Terracotta. Prior to Terracotta, Alex worked at BEA Systems and was Chief Architect at MetaMatrix. His interests include Java, concurrency, distributed systems, query languages, and software design. Alex enjoys tweeting as @puredanger and writing his blog at http://tech.puredanger.com and is a frequent speaker at user group meetings and conferences. In St. Louis, Alex is the founder of the Lambda Lounge group for the study of functional and dynamic languages and the Strange Loop developer conference.
RECO M M ENDED B o o k Developing, testing, and debugging multithreaded programs can still be very difficult; it is all too easy to create concurrent programs that appear to work, but fail when it matters most: in production, under heavy load. Java Concurrency in Practice arms readers with both the theoretical underpinnings and concrete techniques for building reliable, scalable, maintainable concurrent applications. Rather than simply offering an inventory of concurrency APIs and mechanisms, it provides design rules, patterns, and mental models that make it easier to build concurrent programs that are both correct and performant.
books.dzone.com/books/javaconcurrency
Bro ugh t to you by...
BUY NOW
#8
z.co m
it ! V is arz
E: LUD IN C ility TS EN nsib NT spo CO f Re o in d Cha man Com reter rp Inte tor ... ore Itera tor dm dia d an Me rver tho se Me S Ob RN plate TTE Tem
n n n n n n n
rns e t t n Pa g i s De
Cha in e of R ns spo ibil ity, con tinu ed
re le a que st an d th ndle e ha ue req r doe snt st w ith th e ha
one
the e to renc refe listed in ick s A cta qu s, a s NP je rn e b e IG vid s, patt able O pro DES ram . ign us ard UT des diag le f Re refc r oF) ABO mp ts o lass oke erns exa Inv ur (G lemen es c d o Patt d h rl F n f o ig suc s: E inclu go al w n rn Des rn c D a e re e is je ts tt G g AN Th patt , and a t ob mentin l 23 sign Pa M c a h u c M in tr e nd Ea tion ple CO orig ma ons kD re. rma om to c their im boo Softwa nt teC info ed cre Clie the d age : Us d from nd Con () ente on, us ma r ns ct cute Ori Com ) ti uple atte s bje ( +exe low eo lana al P e deco is al p n rg cute ch x o la e . Th s su ati +exe ts. rm an b bject nship c c fo Cre je y an o tio e to ob d as ed rela ms, t th ed eate as rith tha arate : Us be tr ject b ts. m. r ns disp algo c r it to lly ob e te y e e je g s n tt g iv b a sy win ona Pa ana allo traditi s. Rece en o ral en m der uest to m betwe ctu twe nt or req ndled in n. sa sed s varia Stru s be . late catio e ha can s: U or in ilitie psu invo to b llbacks cture that the times Enca quest nsib ca ttern y. stru ips ling re and riant nalit l Pa d respo ing the nsh hand ra pose uing nctio led at va o ct ur cess be fu ue ti io n je P la ob pro av nd ack ,a as q to e the t re e ha eded. b callb nous nality c Beh nships b m ed n ro je ed to fro a ne d y ne ob ynch nctio tio You ne s need at c sts is couple ut an is e as the fu st rela with que s th it itho de e th ar Reque y of re litat pattern ssing w tation articul eals ld be ship or faci p d en ce Use shou tion e: D A hist e. n ed to mman for pro implem ents its ting. ker rela cop runtim co y us m type invo Whe s al pec S e el le e ue s to tu p ex th t id Th ue w ac cla Pro at d im jec ue is are utilizing a job q of the C ueue e que Ob ues with y ged enq dge th que s. B en to han eals me. c roxy Job orithm be giv knowle that is terface D P e : g ct b n ti e in S r le of al ed ca to have d obje of the er mp cop pile rato ut an Exa serv nes ss S at com exec e queue comm Deco Ob confi S Cla e th B d the n . Th for de nge king leto ithin invo hm w Faca cha Sing od tory
n n n n
nd le a outc ay ha an tial hand sm hen oten ject le to . .W le p le ob tern ethod bject be ab tab pat ultip ecific o should cep n M this if the m up the s sp an ac ents ject ime. see passed d is be a plem ks to de to of ob at runt handle e im t co b Use ec se til ld ch ges t n A n ined being shou peats un paren ime ngua erm Whe not e la the runt or if it det ore s re uest som d tion proces e no m req g in metho cep n A e e ar a ndlin e ex ack th ther n ha rown in ndle th ll st until ptio th e ca Exce ion is sm to ha up th tered or ral pt ni ed un le ce ss avio co ex mp echa n pa Beh . is en am Exa he tion uest to has ack. W ject q cep st Ob e re e ex call le th nd th hand s to ha ct obje
ome.
Upcoming Titles
RichFaces Agile Software Development BIRT JSF 2.0 Adobe AIR BPM&BPMN Flex 3 Components
Most Popular
Spring Configuration jQuery Selectors Windows Powershell Dependency Injection with EJB 3 Netbeans IDE JavaEditor Getting Started with Eclipse Very First Steps in Flex
re f c
Download Now
a rd
Refcardz.com
Get
Mo
ef re R
50795
.com
ww
z w. d
Y tutorials, cheatsheets, blogs, feature articles, source code and more. ILIT NSIB
S
Me
diato m
Me
ento
Ob
or
ject
Beh
avio
ral
P RES
succ
ess
9 781934 238622
Version 1.0
Copyright 2009 DZone, Inc. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by means electronic, mechanical, r2 nt dle Clie Han ete publisher. photocopying, or otherwise, without prior written permission C of the Reference: st ( ) oncr que
ern
Con
cre
teH
and () uest
1 ler
+ha
ndle
re
hand
le a
req
uest
ki by lin
ng
ww
z w.d
one
.c o
$7.95
Build
er
State
algo
rit
Stra