

In our “What is Concurrency in Java?” tutorial, we learned that problems can occur within a multi-threaded environment if several threads try to access and change the same data at the same time. This is a serious problem, as it can lead to execution deadlocks and data corruption. A program that allows multiple threads to change the same data structure or objects simultaneously is referred to as being not thread-safe. Conversely, a program that is thread safe prevents other threads from working on the same object when a thread is already working with it. In this programming tutorial, we examine four relatively easy ways of achieving thread safety in our Java programs.
If you missed it, we recommend reading our previous part of this series on Java threading: What is Concurrency in Java?
Jump to:
Using Synchronization in Java
The first way to make a program thread safe is to use Synchronization. Simply put, Synchronization is the process of allowing only one thread at a time to complete a particular operation. It resolves the inconsistency problem by preventing multiple threads from accessing the same resource at the same time. Synchronization uses a synchronized keyword, which is a special modifier that creates a block of code known as a critical section.
Here is an example Java program that increments a value by five on two separate threads:
package com.developer; public class Maths { void add5(int num) { // Create a thread instance Thread t = Thread.currentThread(); for (int i = 1; i <= 5; i++) { System.out.println(t.getName() + " : " + (num + i)); } } } package com.developer; public class Maths2 extends Thread { Maths maths = new Maths(); public void run() { maths.add5(10); } } package com.developer; public class SynchronizationDemo { public static void main(String[] args) { Maths2 maths = new Maths2(); Thread t1 = new Thread(maths); Thread t2 = new Thread(maths); t1.setName("Thread 1"); t2.setName("Thread 2"); t1.start(); t2.start(); } }
As expected, the incremented value jumps all over the place as each thread accesses the same variable concurrently:
Thread 1 : 11 Thread 1 : 12 Thread 1 : 13 Thread 2 : 11 Thread 1 : 14 Thread 2 : 12 Thread 2 : 13 Thread 1 : 15 Thread 2 : 14 Thread 2 : 15
Adding the synchronized keyword to the add5() method resolves this issue:
public class Maths { synchronized void add5(int num) { // Create a thread instance Thread t = Thread.currentThread(); for (int i = 1; i <= 5; i++) { System.out.println(t.getName() + " : " + (num + i)); } } }
Now each thread completes its work in turn:
Thread 1 : 11 Thread 1 : 12 Thread 1 : 13 Thread 1 : 14 Thread 1 : 15 Thread 2 : 11 Thread 2 : 12 Thread 2 : 13 Thread 2 : 14 Thread 2 : 15
Read: Best Tools for Java Mobile Development
Using the volatile Keyword in Java
Another way to achieve thread safety in Java is to employ the volatile keyword. It is a field modifier that ensures that the object can be used by multiple threads at the same time without causing the problematic behaviors mentioned above.
The following example code declares and instantiates two integers using the volatile keyword:
package com.developer; public class VolatileKeywordDemo { static volatile int int1 = 0, int2 = 0; static void methodOne() { int1++; int2++; } static void methodTwo() { System.out.println("int1=" + int1 + " int2=" + int2); } public static void main(String[] args) { Thread t1 = new Thread() { public void run() { for (int i = 0; i < 5; i++) { methodOne(); } } }; Thread t2 = new Thread() { public void run() { for (int i = 0; i < 5; i++) { methodTwo(); } } }; t1.start(); t2.start(); } }
As we can observe in the program output, both variables have been fully incremented by the first thread before the second thread outputs their values:
int1=5 int2=5 int1=5 int2=5 int1=5 int2=5 int1=5 int2=5 int1=5 int2=5
How to Use Atomic Variables in Java
Another way to achieve thread safety in Java is to use atomic variables. As the name suggests, atomic variables allow developers to perform an atomic operation on a variable. Atomic variables minimize synchronization and help avoid memory consistency errors. The most commonly used atomic variables are AtomicInteger, AtomicLong, AtomicBoolean, and AtomicReference. Here is some example Java code that uses AtomicInteger to increment two separate counters before outputting their combined value:
package com.developer; import java.util.concurrent.atomic.AtomicInteger; public class Counter { AtomicInteger counter = new AtomicInteger(); public void increment() { counter.incrementAndGet(); } } package com.developer; public class AtomicIntegerDemo { public static void main(String[] args) throws Exception { Counter c = new Counter(); Thread t1 = new Thread(new Runnable() { public void run() { for (int i = 1; i <= 5000; i++) { c.increment(); } } }); Thread t2 = new Thread(new Runnable() { public void run() { for (int i = 1; i <= 5000; i++) { c.increment(); } } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(c.counter); } }
Running the above program produces an output of “10000“.
Read: Top Java Frameworks
How to Use the final Keyword in Java
Final Variables are always thread safe in Java because, once assigned, a reference to an object cannot point to another object. Here is a short program to demonstrate how to use the final keyword in Java:
package com.developer; public class FinalKeywordDemo { final String aString = new String("Immutable"); void someMethod() { aString = "new value"; } }
Integrated development environments (IDEs) will not even let you run the above code and will show a compiler error about the attempt to reassign a value to the final aString variable:
Final Thoughts on Thread Safety in Java
This programming tutorial presented four ways of achieving thread safety in our Java programs, namely: using Synchronization, the volatile Keyword, via atomic variables, and the final keyword. There are other ways to achieve thread safety in Java, but those require slightly more effort on the part of the developer. These include the use of locks from the java.util.concurrent.locks package and using thread safe collection classes such as ConcurrentHashMap.
Next Steps
Now that you have a firm understanding of some of the ways to achieve thread safety in Java, we recommend checking out a few of our other tutorials on threading and multithreading in Java: