Saturday, May 27, 2017

Top 10 Java Multithreading and Concurrency Best Practices

Writing concurrent code is hard and testing correctness with concurrency is even harder. Though Java programming language provides lots of synchronization and concurrency support from language to API level, it eventually comes to an individual's diligence and expertise to write bug-free Java concurrency code. These Java concurrency and multi-threading best practices are a collection of some well-known tips, which helps you to write better concurrency code in Java. Some of you may be familiar with these tips, it's often worth revising them in a couple of years.

This Java multi-threading and concurrency tips are from my own learning and usage and also inspired by reading books like Effective Java and Java Concurrency in Practice in particular.

I suggest reading Java Concurrency Practice two times to every Java developer, yes, you heard it correctly, TWO times. Concurrency is confusing and difficult to comprehend, much like Recursion to few programmers; and in one reading, you might not get all of it.

And, if you are serious about mastering Java multi-threading and concurrency then I also suggest you take a look at the Java Multithreading, Concurrency, and Performance Optimization course by Michael Pogrebinsy on Udemy. It's an advanced course to become an expert in Multithreading, concurrency, and Parallel programming in Java with a strong emphasis on high performance

Java Multithreading and Concurrency Best Practices

The sole purpose of using concurrency is to produce a scalable and faster program. But always remember, speed comes after correctness. Your Java program must follow its invariant in all conditions, which it would if executed in a sequential manner.

If you are new in concurrent Java programming, then take some time to get familiar yourself with different problems arises due to concurrent execution of program e.g. deadlock, race conditions, livelock, starvation, etc.

1) Use Local Variables

Always try to use local variables instead of creating a class or instance variables. Sometimes, developers use instance variables to save memory and reusing them, because they think creating local variables every time method invoked may take a lot of memory. One example of this is declaring Collection as a member and reusing them by using the clear() method.

This introduces, a shared state in otherwise stateless class, which is designed for concurrent execution. Like in the below code, where execute() method is called by multiple threads, and to implement new functionality, you need a temp collection. In original code, a static List was used and the developer's intention was to clear this at the end of execute() method for reuse.

He thought that code is safe because of CopyOnWriteArrayList is thread-safe. What he failed to realize that, since this method gets called by multiple threads, one thread may see data written by other threads in a shared temp List. Synchronization provided by the list is not enough to protect the method's invariant here.

public class ConcurrentTask{
    private static List temp = Collections.synchronizedList(new ArrayList());
    public void execute(Message message){
        //I need a temporary ArrayList here, use local
        //List temp = new ArrayList();
        //add something from Message into List
        //combine id and code store result back to message
        temp.clear(); // Let's resuse it

Problem :
One Message's data will go to other Message if two call of multiple thread interleaved. e.g. T1 adds Id from Message 1 then T2 adds Id from Message 2, which happens before List get cleared, so one of those message will have corrupted data.

Solution :
1) Add a synchronized block when one thread add something to temp list and clear() it. So that, no thread can access List until one is done with it. This will make that part single threaded and reduce overall application performance by that percentage.

2) Use a local List instead of a global one. Yes it will take few more bytes, but you are free from synchronization and code is much more readable. Also, you should be worrying too much about temporary objects, GC and JIT will take care of that.

This is just one of those cases, but I personally prefer a local variable rather than a member variable in multi-threading, until its part of design.

2) Prefer Immutable Classes

Another and most widely known Java multi-threading best practice is to prefer an Immutable class. Immutable classes like String, Integer and other wrapper classes greatly simplify writing concurrent code in Java because you don't need to worry about there state. Immutable classes reduce the amount of synchronization in code.

Immutable classes, once created, can not be modified. One of the best examples of this is java.lang.String, any modification on String e.g. converting it into uppercase, trim or substring would produce another String object, keeping original String object intact.

3) Minimize locking scope

Any code which is inside lock will not be executed concurrently and if you have 5% code inside lock than as per Amdahl's law, your application performance can not be improved more than 20 times. Main reason of this is those 5% code will always executed sequentially.

You can reduce this amount by minimizing scope of locking, try to only lock critical sections. One of the best example of minimizing scope of locking is double checked locking idiom, which works by using volatile variable after Java 5 improvements on Java Memory model.

Multithreading and Concurrency Best Practices in Java

4) Prefer Thread Pool Executors instead of Threads

Creating Thread is expensive. If you want a scalable Java application, you need to use thread pool. Apart from cost, managing thread requires lots of boiler-plate code and mixing those with business logic reduces readability. Managing threads is a framework level task and should be left to Java or any proprietary framework you are using. JDK has a well built, rich and fully tested Thread pool also known as Executor framework, which should be utilized whenever needed.

5) Prefer Synchronization utility over wait notify

This Java multi-threading practice inspires from Java 1.5, which added lot of synchronization utilities like CycicBariier, CountDownLatch and Sempahore. You should always look to JDK concurrency and synchronization utility, before thinking of wait and notify.

It's much easier to implement producer-consumer design with BlockingQueue than by implementing them using wait and notify. See those two links to compare yourself. Also, it's much easier to wait for 5 threads using CountDownLatch to complete there task rather than implementing same utility using wait and notify. Get yourself familiar with java.util.concurrent package for writing better Java concurrency code.

6) Prefer BlockingQueue for producer-consumer design

This multi-threading and concurrency best practice is related to earlier advice, but I have made it explicitly because of it's importance in real world concurrent applications. Many of concurrency problem are based on producer-consumer design pattern and BlockingQueue is best way to implement them in Java.

Unlike Exchanger synchronization utility which can be used to implement single producer-consumer design, blocking queue can also handle multiple producer and consumers. See producer consumer with BlockingQueue in Java to learn more about this tip.

7) Prefer Concurrent Collections over synchronized Collection

As mentioned in my post about Top 5 Concurrent Collections in Java, they tend to provide more scalablility and performance than there synchronized counterpart. ConcurrentHashMap, which is I guess one of the most popular of all concurrent collection provide much better performance than synchronized HashMap or Hashtable if number of reader thread outnumber writers.

Another advantage of Concurrent collections are that, they are built using new locking mechanism provided by Lock interface and better poised to take advantage of native concurrency construct provided by underlying hardware and JVM. In the same line, consider using CopyOnWriteArrayList in place of synchronized List, if List is mostly for reading purpose with rare updates.

8) Use Semaphore to create bounds

In order to build a reliable and stable system, you must have bounds on resources like database, file system, sockets etc. In no situation, your code create or use infinite number of resources. Semaphore is a good choice to have a limit on expensive resource like database connection, by the way leave that to your Connection pool. Semaphore is very helpful to creating bounds and blocking thread if resource is not available. You can follow this tutorial to learn how to use use Semaphore in Java.

9) Prefer synchronized block over synchronized method

This Java multi-threading best practice is an extension of earlier best practice about minimizing scope of locking.  Using synchronized block is one way to reduce scope of lock and it also allow you to lock on object other than "this", which represent current object. Today, your first choice should be atomic variable, followed by volatile variable if your synchronization requirement is satisfied by using them.

Only if you need mutual exclusion you can consider using ReentrantLock followed by plain old synchronized keyword. If you are new to concurrency and not writing code for high-frequency trading or any other mission critical application, stick with synchronized keyword because its much safer and easy to use. If you are new to Lock interface, see my tutorial how to use Lock in multi-threaded Java program for step by step guide.

10) Avoid Using static variables

As shown in the first multi-threading best practice, static variables can create lots of issues during concurrent execution. If you happen to use a static variables, consider it making static final constants, and if static variables are used to store collections like List or Map then consider using only read only collections. If you are thinking of reusing Collection to save memory, please see the example in first best practice to learn how static variables can cause problem in concurrent programs.

11) Prefer Lock over synchronized keyword

This is a bonus multi-threading best practice, but it's double edge sword at the same time. Lock interface is powerful but every power comes with responsibility. Different locks for reading and write operation allows us to build scalable data structures like ConcurrentHashMap, but it also require lot of care during coding.

Unlike synchronized keyword, the thread doesn't release lock automatically. You need to call unlock() method to release a lock and best practice is to call it on finally block to ensure release in all conditions. here is an idiom to use explicitly lock in Java :

try {
    //do something ...
} finally {

By the way, this article is in line with 10 JDBC best practices and 10 code comments best practices, if you haven't read them already, you may find them worth reading. As some of you may agree that there is no end of best practices, It evolves and gets popular with time. If you guys have any advice, experience, which can help anyone writing concurrent programs in Java, please share.

That's all on this list of Java multithreading and concurrency best practices. Once again, reading Concurrency Practice in Java and Effective Java is worth reading again and again. Also developing a sense for concurrent execution by doing code review helps a lot with visualizing problems during development. On a closing note, let us know what best practices you follow while writing concurrent applications in Java?

Further Learning
Multithreading and Parallel Computing in Java
Java Concurrency in Practice - The Book
Applying Concurrency and Multi-threading to Common Java Patterns
Java Concurrency in Practice Course by Heinz Kabutz



Hi Javin ,

Just want to add with respect to If you're simply locking an object, I'd prefer to use synchronized


doSomethingNifty(); // Throws a NPE!
Lock.release(); // Oh noes, we never release the lock!
You have to explicitly do try{} finally{} everywhere.

Whereas with synchronized, it's super clear and impossible to get wrong:

synchronized(myObject) {
That said, Locks may be more useful for more complicated things where you can't acquire and release in such a clean manner. I would honestly prefer to avoid using bare Locks in the first place, and just go with a more sophisticated concurrency control such as a CyclicBarrier or a LinkedBlockingQueue, if they meet your needs.

I've never had a reason to use wait() or notify() but there may be some good ones.


Also please check the imple Lock implementation:

public class Lock{

private boolean isLocked = false;

public synchronized void lock()
throws InterruptedException{
isLocked = true;

public synchronized void unlock(){
isLocked = false;

Notice the while(isLocked) loop, which is also called a "spin lock". Spin locks and the methods wait() and notify() are covered in more detail in the text Thread Signaling. While isLocked is true, the thread calling lock() is parked waiting in the wait() call. In case the thread should return unexpectedly from the wait() call without having received a notify() call (AKA a Spurious Wakeup) the thread re-checks the isLocked condition to see if it is safe to proceed or not, rather than just assume that being awakened means it is safe to proceed. If isLocked is false, the thread exits the while(isLocked) loop, and sets isLocked back to true, to lock the Lock instance for other threads calling lock()

Post a Comment