Thread Safety in Java is a very important topic. Java provides multi-threaded environment support using Java Threads, we know that multiple threads created from same Object share object variables and this can lead to data inconsistency when the threads are used to read and update the shared data.
The reason for data inconsistency is because updating any field value is not an atomic process, it requires three steps; first to read the current value, second to do the necessary operations to get the updated value and third to assign the updated value to the field reference. Let’s check this with a simple program where multiple threads are updating the shared data.
package com.journaldev.threads;
public class ThreadSafety {
public static void main(String[] args) throws InterruptedException {
ProcessingThread pt = new ProcessingThread();
Thread t1 = new Thread(pt, "t1");
t1.start();
Thread t2 = new Thread(pt, "t2");
t2.start();
//wait for threads to finish processing
t1.join();
t2.join();
System.out.println("Processing count="+pt.getCount());
}
}
class ProcessingThread implements Runnable{
private int count;
@Override
public void run() {
for(int i=1; i < 5; i++){
processSomething(i);
count++;
}
}
public int getCount() {
return this.count;
}
private void processSomething(int i) {
// processing some job
try {
Thread.sleep(i*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
In the above program for loop, count is incremented by 1 four times and since we have two threads, its value should be 8 after both the threads finished executing. But when you will run the above program multiple times, you will notice that count value is varying between 6,7,8. This is happening because even if count++ seems to be an atomic operation, its NOT and causing data corruption.
Thread safety in java is the process to make our program safe to use in multithreaded environment, there are different ways through which we can make our program thread safe.
Synchronization is the tool using which we can achieve thread-safety, JVM guarantees that synchronized code will be executed by only one thread at a time. java keyword synchronized is used to create synchronized code and internally it uses locks on Object or Class to make sure only one thread is executing the synchronized code.
synchronized(this)
will lock the Object before entering into the synchronized block.Here are the code changes we need to do in the above program to make it thread-safe.
//dummy object variable for synchronization
private Object mutex=new Object();
...
//using synchronized block to read, increment and update count value synchronously
synchronized (mutex) {
count++;
}
Let’s see some synchronization examples and what can we learn from them.
public class MyObject {
// Locks on the object's monitor
public synchronized void doSomething() {
// ...
}
}
// Hackers code
MyObject myObject = new MyObject();
synchronized (myObject) {
while (true) {
// Indefinitely delay myObject
Thread.sleep(Integer.MAX_VALUE);
}
}
Notice that hacker’s code is trying to lock the myObject instance and once it gets the lock, it’s never releasing it causing doSomething() method to block on waiting for the lock, this will cause the system to go on deadlock and cause Denial of Service (DoS).
public class MyObject {
public Object lock = new Object();
public void doSomething() {
synchronized (lock) {
// ...
}
}
}
//untrusted code
MyObject myObject = new MyObject();
//change the lock Object reference
myObject.lock = new Object();
Notice that lock Object is public and by changing its reference, we can execute synchronized block parallel in multiple threads. A similar case is true if you have private Object but have a setter method to change its reference.
public class MyObject {
//locks on the class object's monitor
public static synchronized void doSomething() {
// ...
}
}
// hackers code
synchronized (MyObject.class) {
while (true) {
Thread.sleep(Integer.MAX_VALUE); // Indefinitely delay MyObject
}
}
Notice that hacker code is getting a lock on the class monitor and not releasing it, it will cause deadlock and DoS in the system. Here is another example where multiple threads are working on the same array of Strings and once processed, appending thread name to the array value.
package com.journaldev.threads;
import java.util.Arrays;
public class SyncronizedMethod {
public static void main(String[] args) throws InterruptedException {
String[] arr = {"1","2","3","4","5","6"};
HashMapProcessor hmp = new HashMapProcessor(arr);
Thread t1=new Thread(hmp, "t1");
Thread t2=new Thread(hmp, "t2");
Thread t3=new Thread(hmp, "t3");
long start = System.currentTimeMillis();
//start all the threads
t1.start();t2.start();t3.start();
//wait for threads to finish
t1.join();t2.join();t3.join();
System.out.println("Time taken= "+(System.currentTimeMillis()-start));
//check the shared variable value now
System.out.println(Arrays.asList(hmp.getMap()));
}
}
class HashMapProcessor implements Runnable{
private String[] strArr = null;
public HashMapProcessor(String[] m){
this.strArr=m;
}
public String[] getMap() {
return strArr;
}
@Override
public void run() {
processArr(Thread.currentThread().getName());
}
private void processArr(String name) {
for(int i=0; i < strArr.length; i++){
//process data and append thread name
processSomething(i);
addThreadName(i, name);
}
}
private void addThreadName(int i, String name) {
strArr[i] = strArr[i] +":"+name;
}
private void processSomething(int index) {
// processing some job
try {
Thread.sleep(index*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Here is the output when I run the above program.
Time taken= 15005
[1:t2:t3, 2:t1, 3:t3, 4:t1:t3, 5:t2:t1, 6:t3]
The String array values are corrupted because of shared data and no synchronization. Here is how we can change addThreadName() method to make our program thread-safe.
private Object lock = new Object();
private void addThreadName(int i, String name) {
synchronized(lock){
strArr[i] = strArr[i] +":"+name;
}
}
After this change, our program works fine and here is the correct output of the program.
Time taken= 15004
[1:t1:t2:t3, 2:t2:t1:t3, 3:t2:t3:t1, 4:t3:t2:t1, 5:t2:t1:t3, 6:t2:t1:t3]
That’s all for thread safety in java, I hope you learned about thread-safe programming and using synchronized keyword.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
Hi, I tried example SyncronizedMethod several times. It doesn’t corrupt the String Array and I am geting the o/p [1:t1:t3:t2, 2:t1:t3:t2, 3:t1:t3:t2, 4:t1:t3:t2, 5:t1:t3:t2, 6:t1:t3:t2] evry time I run the program. I don’t understand the reason. Doesn’t seems need of using synchronization.
- Namita Rode
Nice explanation of core thread concepts. Great work sir !!.
- babanna s d
You forgot to call pt.start(); in the first example…
- Coder
package com.journaldev.threads; public class ThreadSafety { public static void main(String[] args) throws InterruptedException { ProcessingThread pt = new ProcessingThread(); Thread t1 = new Thread(pt, “t1”); t1.start(); Thread t2 = new Thread(pt, “t2”); t2.start(); //wait for threads to finish processing t1.join(); t2.join(); System.out.println(“Processing count=”+pt.getCount()); } } class ProcessingThread implements Runnable{ private int count; @Override public void run() { for(int i=1; i < 5; i++){ processSomething(i); count++; } } public int getCount() { return this.count; } private void processSomething(int i) { // processing some job try { Thread.sleep(i*1000); } catch (InterruptedException e) { e.printStackTrace(); } } } When I test the code with Idea tools, the result is always equal to 8. Can you tell me why?
- jeesk
Examples with ` java.util.concurrent.lock` are also welcomed.
- Eddy
Quote: ________________________________________________________________________________________________________ public class MyObject { //locks on the class object’s monitor public static synchronized void doSomething() { // … } } // hackers code synchronized (MyObject.class) { while (true) { Thread.sleep(Integer.MAX_VALUE); // Indefinitely delay MyObject } } Notice that hacker code is getting lock on class monitor and not releasing it, it will cause deadlock and DoS in the system. ______________________________________________________________________________________________________ This may not really correct. Please see the code below: _________________________________________________________________________________________________ public class Hack { public static void main(String[] args) throws Exception{ new Thread(new R1()).start(); Thread.sleep(1000); new Thread(new R2()).start(); new Thread(new R3()).start(); } } class MyObject { public void sayHello(){ synchronized(MyObject.class){ System.out.println(“C: Hello, Word”); } } public synchronized void sayHello2(){ System.out.println(“M: Hello, Word”); } } class R1 implements Runnable{ @Override public void run(){ synchronized (MyObject.class) { System.out.println(“Lock class instance!”); while (true) { try { Thread.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } } } } } class R2 implements Runnable{ @Override public void run() { MyObject myObject = new MyObject(); myObject.sayHello(); } } class R3 implements Runnable{ @Override public void run() { MyObject myObject = new MyObject(); myObject.sayHello2(); } } ___________________________________________________________________________________________________ Result: ______________________________ Lock class instance! M: Hello, Word ______________________________ see more: https://stackoverflow.com/a/2056256/2893073 - Thanks!
- Eddy
alert(“hiiiiii”);
- asgh
nice explanation about synchronization. thank you
- Instanceofjava
When we lock an Object, it acquires lock on all the fields of the Object. Can u please explain me this sentence… the way i understood is ,If i have a object A and object B is a field of Object A.It aquires lock on object B also… Is that correct
- Naveen J