Skip to main content

Some Java Concurrency Tips

Posted by caroljmcdonald on September 17, 2009 at 1:55 PM PDT

Here is a review of some concurrency tips from Joshua Bloch, Brian
Goetz and others.

Prefer immutable objects/data

Immutable objects do not change after construction. Immutable objects
are simpler, safer, require no locks, and are thread safe. To
make an object immutable don't provide setters/mutator methods, make
fields private final, and prevent subclassing. If immutability is not an option, limit mutable
state, less mutable state means less coordination.  Declare fields
final wherever practical, final fields are simpler than mutable fields.

When threads share mutable data, each thread that reads or writes must
coordinate access to the data. Failing to synchronize shared mutable
data can lead to atomicity failures, race conditions, inconsistent
state, and other forms of non-determinism. These erratic problems are among
the most difficult to debug.


Limit concurrent interactions to well defined points, limit shared
data, consider copying instead of sharing.

Threading risks for Web applications

A Servlet get, post, service method can be called for multiple clients at the same
time. Multi-threaded Servlet Instance and Static variables are shared
and therefore if mutable, access must be coordinated. Servlets are
typically long-lived objects with a high thread load, if you
over-synchronize performance suffers, try to either share immutable
(final) data, or don’t share at all, request arguments and local variables are safer.

Hold Locks for as short a time
as possible


Do as little work as possible inside synchronized
regions.  Move code that doesn't require the
lock out of synchronized block, especially if 
time-consuming(!).



The Lock interface provides more extensive locking operations than
using a synchronized block, one advantage is the ability to not block
if a lock is not available.  You should obtain the lock, read or
write shared data only as
necessary, and unlock within a finally clause to ensure that the lock
is released. Below is an example using a ReentrantReadWriteLock:




A way to reduce the time that a lock is held is lock splitting or
lock striping, which uses different locks for state variables instead
of a single lock. This reduces the lock granularity, allowing greater
scalability but you must take locks in a disciplined order or
risk deadlock.


Prefer executors and tasks to threads


Instead of working directly with threads, use the Java Concurrency
Utilities Executor Framework. The Executor service decouples task
submission from execution policy. Think in terms of runnable tasks and
let an executor service execute them for you.





Executors can be created either directly or by using the factory
methods in the Executors class:








Here is an example that uses the Executor, Executors and
ExecutorService classes:


The example is a web service class that handles multiple incoming
connections simultaneously with a fixed pool of threads. A fixed
thread pool is initialized with the newFixedThreadPool method of the
Executors class which returns an ExecutorService object. Incoming
connections are handled by calling execute on the ExecutorService pool
object, passing it a Runnable object. The Runnable object's run method
processes the connection. When the run method completes the thread will
automatically be returned to the thread pool. If a connection comes in
and all
threads are in use, then the main loop will block until a thread is
freed.

Prefer Concurrency utilities to wait
and notify


Whenever you are about to use wait and notify check and see if there is a class in
java.util.concurrent that does what you need.  The concurrent
collections provide high-performance concurrent
implementations of standard collection interfaces such as List, Queue,
and Map.





BlockingQueues are concurrent queues extended with blocking methods,
which wait (or block) until an element becomes available for retrieving
or space becomes available for storing.



Producer Consumer Pattern



Blocking queues are useful for the Producer Consumer Pattern where
producer threads enqueue work items and consumer threads dequeue and
process work items. Below is an example of a Consumer Pattern for a
logger used by multiple threads. The Logger constructor takes a
BlockingQueue as an input argument. In the run method messages
are retrieved from the
queue and logged. When the queue is empty the logging thread
will block until an message becomes available for retrieving.



Below
is an example of a Producer that uses the logger. A new
ArrayBlockingQueue is
instantiated for passing to the logger constructor. In the run
method messages are put into the queue for logging. If the queue
is
full, the put will block until the logger has removed messages.



Synchronizers



Synchronizers are objects that help with coordinating access between threads. The most used
synchronizers are CountDownLatch and Semaphore.  The use of synchronizers can eliminate most uses of
wait or notify.




Below is an example
of the use of a semaphore to control access to pool of resources.
Multiple threads can request the use of a resource and return it when
they have finished with it.



In the creator we create a new semaphore with the same size as the pool of resources we're creating.


In the getResource() method the semaphore aquire method is called
to try to aquire a permit to use a resource. If there
are resources available this will return and a resource will be
returned from the pool. If all the resources are in use the call to
aquire will block until another thread calls release on the
semaphore.  When a thread finishes with a resource the resource is
returned to the
pool and the release method is called. Both aquire and release can be
considered atomic operations.





Multithreaded Lazy Initialization is tricky

When threads share a lazily initialized field, access to the field must
be synchronized, or non-determinism type bugs can result.



src="http://blogs.sun.com/carolmcdonald/resource/lazyinit.jpg">

Prefer Normal initialization

Don't
use lazy initialization unless an object or field is costly to
initialize and not used often. Normally normal initialization is best
;) Below is a thread safe example of eager initialization for a
singleton, the private final instance field and the private constructor
make it immutable.





If you need to use lazy initialization for performance on a
static field, use the initialize-on-demand holder pattern. This pattern takes advantage of the guarantee that a class will
not be initialized until it is used
:


References and More Information:

Effective
Java
, Second Edition by Joshua Bloch

Java
Concurrency in Practice
by Brian Goetz

Robust
and Scalable Concurrent Programming: Lessons from the Trenches

Concurrency:
Past and Present







Related Topics >>

Comments

The pictures are not showing up any more...

The pictures are not showing up any more...

Simple & Well Explained

Written in very simple language & very well explained ,blog is very well compiled & i like the way important points are distinguished using color ,the last section lazy initialization is really helpful & explained very well.

Good

Hi, I liked the blog very much. Gathered good knowledge about concurrency. Thanks for the same. Keep posting :)