Thread Safety and How to Achieve it in Java
Before diving into the topic, let’s just recap the concept of Multithreading provided by Java where we can create and execute multiple threads of the same object. When these multiple threads share the same object variables, concerns are raised over the safety of each thread due to inconsistent results.
The inconsistent results are obtained while multiple threads are updating the same field value. We need to make sure the Thread Safety by preventing another thread from performing operations while the current thread is working on the same object.
Let’s look at an example to understand more.
Example code:
Java
public class ThreadSafetyExample {
public static void main(String[] s) throws InterruptedException {
ProcessingThread p = new ProcessingThread();
Thread one = new Thread(p, "one");
one.start();
Thread two = new Thread(p, "two");
two.start();
// we need to wait for threads one and twoto finish their //processing
one.join();
two.join();
System.out.println("Processing count="+p.getCount());
}
}
class ProcessingThreadExample implements Runnable{
private int ct;
@Override
public void run() {
for(int i=0; i <= 4; i++){
processSomething(i);
ct++;
}
}
public int getCount() {
return this.ct;
}
private void processSomething(int i) {
// processing a defined job
try {
Thread.sleep(i*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Here, we can see in the above program that, the for loop incremented ct value four times by one unit so that its value should become 8 after the completion of execution of threads one and two. Upon executing this program, we end up with the ct value varying between 6, 7, and 8 due to the corruption of the data or inconsistency of the field. This made the Thread unsafe in Java which may end up producing unexpected results.
How to Achieve Thread Safety in Java
Java comes with four ways to make sure the Thread Safety and they are:
- Synchronization mechanism.
- By the usage of Volatile Keyword.
- By the usage of Atomic Variable.
- By the usage of Final Keyword.
Now let’s see each method to make sure the Thread Safety.
Synchronization Mechanism
Thread Safety can be achieved by the method of synchronization where we use the Java keyword synchronized which creates synchronized code and locks the resource from another thread while the current thread is executing the synchronized code.
We use the keyword synchronized to make a method that is synchronized or we can also create a synchronized block where only some sections of the method need synchronization. While making a method synchronized, first the object gets locked and if the method is static, then the class gets locked.
When there are multiple blocks in a class that is synchronized and an object was locked by one of them, the remaining synchronized blocks cannot be executed by other threads as when an object acquires a lock, all the variables and fields also get locked.
Let’s look at an example program of synchronization to ensure Thread Safety
Java:
class One {
synchronized void sum(int n)
{
// Creation of an instance for a thread
Thread ct = Thread.currentThread();
for (int i = 1; i <= 5; i++) {
System.out.println(
ct.getName() + " : " + (n + i));
}
}
}
// Class Two extending the thread class
class Two extends Thread {
// Creating an object of class One
One obj = new One();
public void run()
{
// Invoking the method sum()
obj.sum(1);
}
}
class Test {
public static void main(String[] args)
{
// Creation of an object for class Two
Two obj2 = new Two();
// Initialization of the instance t1 of Thread
// class with the object of class Two
Thread t1 = new Thread(Two);
// Initialization of the instance t2 of Thread
// class with the object of class Two
Thread t2 = new Thread(Two);
// Initialization of thread t1 with name
//'Thread One'
t1.setName("Thread One");
// Initialization of thread t2 with name
//'Thread Two'
t2.setName("Thread Two");
// Starting the thread instances t1 and t2
t1.start();
t2.start();
}
}
Output:
Thread One : 2
Thread One : 3
Thread One : 4
Thread One : 5
Thread One : 6
Thread Two : 2
Thread Two : 3
Thread Two : 4
Thread Two : 5
Thread Two : 6
Usage of Volatile Keyword
It is another way of achieving Thread Safety, where the object is available to multiple threads at a time without leading to inconsistency. Java provides a keyword, volatile, which is a field modifier, which makes this process happen and ensures the safety of the thread.
Example code:
Java:
public class VolatileExample {
// Initializaton of the volatile variables
// x y
static volatile int x = 0, y = 0;
// Defining a static void method
static void methodone()
{
x++;
y++;
}
// Defining another static void method
static void methodtwo()
{
System.out.println(
"x=" + x + " y=" + y);
}
public static void main(String[] s)
{
// Creation of an instance one of
// the Thread class
Thread one = new Thread() {
public void run()
{
for (int i = 0; i < 5; i++)
methodone();
}
};
// Creation of an instance two of
// Thread class
Thread two = new Thread() {
public void run()
{
for (int i = 1; i <= 5; i++)
methodtwo();
}
};
// Starting the instances t1 and t2
one.start();
two.start();
}
}
Output:
x=5 y=5
x=5 y=5
x=5 y=5
x=5 y=5
x=5 y=5
Usage of Atomic Variable
We can also achieve Thread Safety by using an atomic variable in our Java program. It makes the multiple threads run concurrently without any interference.
Just look at an example program to understand clearly.
Java:
import java.util.concurrent.atomic.AtomicInteger;
class Counter1 {
// Creation of a variable that is
// of the class type AtomicInteger
AtomicInteger ct
= new AtomicInteger();
// Defining the method of increment()
// to make the value of
// AtomicInteger variable change
public void increment()
{
ct.incrementAndGet();
}
}
public class TestAtomic {
public static void main(
String[] s) throws Exception
{
// Creation of an instance of
// the Counter class
Counter obj = new Counter1();
// Creation of an instance one of
// the Thread class
Thread one = new Thread(
new Runnable() {
public void run()
{
for (int i = 1; i <= 4000; i++) {
obj.increment();
}
}
});
// Creation of an instance two
// of the Thread class
Thread two = new Thread(
new Runnable() {
public void run()
{
for (int i = 1; i <= 4000; i++) {
obj.increment();
}
}
});
// Calling the start() method with one and two thread instances
one.start();
two.start();
// Calling the join method with one and two thread instances
one.join();
two.join();
System.out.println(obj.ct);
}
}
Output:
8000
Usage of the Final keyword
We can also make sure of the safety of a thread by using the final variables. By using the Java keyword final, we can assign a permanent reference to a variable, and then once it is assigned, later it cannot reference another object and returns an error if we try to change its value.
Look at an example program.
Java:
// Program to depict the Use of the Final Keyword
// The Main class
class Finalclass {
// the driver main method
public static void main(String s[])
{
// variable v which is defined using final keyword
final int v = 50;
v = 60;
// this program generates error as we tried to change the final // variable
}
}
Output:
Compilation Error in java code :-
prog.java:14: error: cannot assign a value to final variable v
v = 60;
^
1 error
Here, we can observe that, when we tried to change the value of the final variable, the compiler produced an error as the final variable cannot refer to another object once it is assigned with an object initially.