Java threading — best practices

When I first started using threads in Java, I couldn’t find any guide, any “best practices”. I had to sort out my own.

I had seen the data damage caused by failure to synchronize threads, and the delays and mess that result from willy-nilly synchronization. I had decided the answer was to clearly identify where information had to be shared between threads, and to synchronize only where necessary. This remains good advice, but the story has become richer.

Since then, many articles on how to avoid the worst problems have appeared, and the Java packages have changed, to make robust programming easier and to give options for faster execution.

I went through some of the main articles and collected what struck me as the main points.

There are issues with the singleton pattern in multi-threaded environments. Depending on what you want them to do, there are multiple ways to make thread-safe ones.

How you know you’re in trouble

If you’re thinking in terms of tossing in more synchronized keywords, if you’re thinking in terms of synchronizing enough or synchronizing more, you don’t know what you’re doing.

Synchronization should be applied precisely where it is needed, and nowhere else. The discipline is to know what those places are.

Results of incorrect synchronization

Failure to synchronize: mangled data, erratic behavior, crashes, etc.

Inappropriate synchronization: very slow operation, pauses, hangs.

Avoiding race conditions, deadlock and other bad behavior

use local variables in execute() rather than instance/class/static variables.
That is, thread storage should be instantiated in execute() so that it can’t be available to other threads.
Otherwise, the variable’s object be somehow guaranteed to be thread-safe.
Most commonly: final and immutable variables.
Special cases: Atomic classes, and volatile variables.
after using lock(), always unlock().
        lock.lock();
        try {
                        // do something ...
        } finally {
                lock.unlock();
	} 
never call wait() outside a loop that checks a wait condition!

The purpose is to pause execution of the current thread until something has occurred in another thread. Use a “guarded block”, like

	synchronized
	{
		while( !condition )
		{
			try
			{
				wait();
			}
			catch( InterruptedException e )
			{}
		}
	} 

In the other thread where condition is altered, a synchronized call to notifyAll() will cause wait() in the above to return, so that condition can be checked and used in the first thread.

Right behavior and efficiency

minimize locking scope
This is Rule Number 1.
Much easier to see what must be concurrent in the code.
About efficiency, see Amdahl’s law
prefer synchronized blocks to synchronized methods
Synchronized methods synchronize the current object this.
(synchronized methods of static objects synchronize the object class!)

Efficiency

code to be accessed by different threads should be locked with separate locks
(that is, pass different mutex objects in the synchornized keyword)
thread pool executors instead of explicitly creating threads
(if lots of threads are to be created) thread creation is expensive.
new synchronization utilities rather than wait/notify
e.g. CycicBarrier, CountDownLatch and Sempahore
see java.util.concurrent
Especially BlockingQueue for producer-consumer design.
concurrent collections rather than synchronized collections
E.g. ConcurrentHashMap rather than a synchronized HashMap.
ReentrantLock
(for high performance code) Lots of tweeking options.
see java.util.concurrent.locks
Can choose “unfair” lock, which may be faster with multiple threads.
There are other features, such as interruptible locks, try locks, coupleable locks...
Callable vs. Runnable
(when thread needs to communicate a lot with the invoking thread)

Persistence

(EJB) max thread count server setting
Threads consume server resources,
but if max thread count is lower than the number used by application, performance can be affected.
(? how to debug this?)
(EJB) @Singleton
bean maintains state (and identity?) between client invocations

Thread-safe singletons

Usually have a public maker function, to insure the private constructor is called just once, based on the value of a flag. But what if that function is called from multiple threads?

familiar thread-safe singleton.

Instantiates at class load time.

	public class S
	{
		private static final S theInstance = new S();

		private
		S() { }

		public static S
		getInstance()
		{
			return theInstance;
		}
	} 

enum singleton.

Advantage: no need to hide the constructor or provide a getInstance. It may be just superior to the above familiar version.

The constructor can take only class-load-time arguments.

	public enum S
	{
		theInstance;

		S() { }
	} 

“double-checked locking” singleton

Thread-safe, fast, lazy-instantiated.
Run-time constructor could use run-time info.

	public class S
	{
		private static S theInstance;

		private
		S() { }

		public static S
		getInstance()
		{
			if( theInstance == null )
			{
				synchronized( S.class )  // first invocation only!
				{
					if( theInstance == null ) 
						theInstance = new S(); 
				}
			}
			return theInstance;
		}
	} 

Interfaces

All the above advice is at the code level, and naturally dependent upon discipline, and therefore fragile. It is very easy to insert another bit of code that does some unsafe communication between threads. There are better methodologies that do away with much of that magic.

Programming to an interface is useful here: by exposing only very limited interfaces between communicating threads, the task of checking that code is thread-safe is greatly simplified. It requires only that the programmer carefully define the communications relationship between the two threads.

The idea is then to identify very limited regions of code in the current thread that might be accessed by other threads, and make an interface that exposes only that. Another thread only accesses the current thread only via this interface. In Java, this results in very tight access control.

For example, it is very common for two threads to communicate as though one is a server and the other is a client. Here a client-server model is in order: one thread sees the other as a client, the other sees the first as a server. The methods requiring synchronization are very clear.

The flip-side of this is: it is very poor practice to expose non-thread-safe methods to another thread at all.


See:

Top 10 Java Multithreading and Concurrency Best Practices

Modern threading for not-quite-beginners

Deadlock anti-patterns

The Java Tutorials: Guarded Blocks