x Java Java 8 JUnit JSON
  • XML
  • JDBC Spring Boot Microservices React Contact Us

    Thread Synchronization

    You can achieve thread synchronization in Java using synchronized keyword. You can use it in two forms: synchronized blocks and synchronized methods.

    Race Conditions

    Threads share memory, and they can concurrently modify data which can lead to data corruption. When two or more threads are trying to access a variable and one of them wants to modify it, you get a problem known as a race condition.

    Race Condtion example

    In below example multiple threads are trying to increase the value counter variable. You may expect the counter to increase always by a value of one and print like 1 2 3 4..., but it may not be always possible as multiple threads can access the counter variable at the same time and increase its value. If you are not able to reproduce try increasing sleep time interval to 1 sec.

    1) Java example without using thread synchronization

    class Counter {
    
        private int counter;
        
        public void increaseCounter() {
            try {
                Thread.sleep(300); // Added sleep to mimic time consuming operation
            } catch (InterruptedException e) {
                e.printStackTrace();
            }    
        
            this.counter++;
            System.out.println("Counter value is " + this.counter);
        }
    
    }    
    
    public class CounterThread implements Runnable {
        private Counter counter;
        
        public CounterThread(Counter counter) {
            this.counter = counter;
        }
    
        public void run() { 
        	for (int i=0; i< 5; i++) {
                counter.increaseCounter();
        	}
    
        }
        
        public static void main(String[] args) {
            Counter counter = new Counter();
            CounterThread counterThread = new CounterThread(counter);
            Thread thread1 = new Thread(counterThread); 
            Thread thread2 = new Thread(counterThread); 
            Thread thread3 = new Thread(counterThread); 
            thread1.start();
            thread2.start();
            thread3.start();
        }
    
    }
    	

    Output :

    Counter value is 1
    Counter value is 3
    Counter value is 3
    Counter value is 4
    Counter value is 6
    Counter value is 6
    Counter value is 7
    Counter value is 8
    Counter value is 9
    Counter value is 10
    Counter value is 11
    Counter value is 12
    Counter value is 13
    Counter value is 15
    Counter value is 15		

    2) Example of thread synchronization in java

    You can easily fix above race condition problem by using synchronized keyword.

    class Counter {
    
        private int counter;
        
        public synchronized void increaseCounter() {
            try {
                Thread.sleep(300); // Added sleep to mimic time consuming operation
            } catch (InterruptedException e) {
                e.printStackTrace();
            }    
        
            this.counter++;
            System.out.println("Counter value is " + this.counter);
        }
    
    }    
    
    public class CounterThread implements Runnable {
        private Counter counter;
        
        public CounterThread(Counter counter) {
            this.counter = counter;
        }
    
        public void run() { 
        	for (int i=0; i< 5; i++) {
                counter.increaseCounter();
        	}
    
        }
        
        public static void main(String[] args) {
            Counter counter = new Counter();
            CounterThread counterThread = new CounterThread(counter);
            Thread thread1 = new Thread(counterThread); 
            Thread thread2 = new Thread(counterThread); 
            Thread thread3 = new Thread(counterThread); 
            thread1.start();
            thread2.start();
            thread3.start();
        }
    
    }
    	

    Output :

    Counter value is 1
    Counter value is 2
    Counter value is 3
    Counter value is 4
    Counter value is 5
    Counter value is 6
    Counter value is 7
    Counter value is 8
    Counter value is 9
    Counter value is 10
    Counter value is 11
    Counter value is 12
    Counter value is 13
    Counter value is 14
    Counter value is 15	

    You can also fix above race condition problem by using synchronized keyword at block level.

    class Counter {
    
        private int counter;
        
        public void increaseCounter() {
            try {
                Thread.sleep(300); // Added sleep to mimic time consuming operation
            } catch (InterruptedException e) {
                e.printStackTrace();
            }    
            
            synchronized(this) {
                this.counter++;
                System.out.println("Counter value is " + this.counter);
            }
            
        }
    
    }       

    AtomicInteger in Java

    Note that marking increaseCounter() method as synchronized will cause performance issues as getting and releasing a lock may be more time consuming than the actual work. Instead we can use AtomicInteger to solve the race condition.

    AtomicInteger is typically used in multithreading to achieve synchronization using compare-and-swap (CAS)mechanism. Atomic variables are finer-grained and lighter weight than locks and ideal to use as counters, sequence generator etc. AtomicInteger provides far better performance than using explicit locks where you need to update single variable of type int in a multithreading environment.

    Below is a thread-safe replacement for the Counter class shown in the previous example:

    import java.util.concurrent.atomic.AtomicInteger;
    	
    class Counter {
    
        private AtomicInteger count = new AtomicInteger();
        
        public void increaseCounter() {
            count.getAndIncrement(); // atomic operation    
            System.out.println("count value is " + this.count);
        }
    
    }    
    
    public class CounterThread implements Runnable {
        private Counter counter;
        
        public CounterThread(Counter counter) {
            this.counter = counter;
        }
    
        public void run() { 
        	for (int i=0; i< 5; i++) {
                counter.increaseCounter();
        	}
        }
        
        public static void main(String[] args) {
            Counter counter = new Counter();
            CounterThread counterThread = new CounterThread(counter);
            Thread thread1 = new Thread(counterThread); 
            Thread thread2 = new Thread(counterThread); 
            Thread thread3 = new Thread(counterThread); 
            thread1.start();
            thread2.start();
            thread3.start();
        }
    
    }	

    Output :

    count value is 1
    count value is 2
    count value is 3
    count value is 4
    count value is 5
    count value is 6
    count value is 7
    count value is 8
    count value is 9
    count value is 10
    count value is 11
    count value is 12
    count value is 13
    count value is 14
    count value is 15		

    Reference: Java 8 Docs AtomicInteger
    Comments

    Leave a Reply

    Your email address will not be published. Required fields are marked *











    Share This