Java Programming Tutorials

Java programming tutorials with many code examples!

Java synchronized method

Problem:

How to safely modify data in multi-threaded Java code? This problem can be solved in many ways. In this post we’re going to show how to use Java synchronized method to guarantee code correctness.

In the following code we are creating one Counter, that will make sure all work is done, but not more. To make the program fast we’ll create 20 threads that will do the work until it is finished:

package com.farenda.java.lang;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadSynchronizedMethods {

    private static class Counter {
        private int number;

        public Counter(int number) {
            this.number = number;
        }

        public boolean dec() {
            // step 1: comparison
            if (number > 0) {
                // speed up unwanted modifications by other threads:
                Thread.yield();
                // step 2: modification
                --number;
                return true;
            }
            return false;
        }
    }

    private static class Worker implements Runnable {

        private static int nth = 0;

        private final int id = ++nth;
        private Counter counter;

        public Worker(Counter counter) {
            this.counter = counter;
        }

        @Override
        public void run() {
            System.out.println("Starting worker: " + id);
            while (counter.dec()) {
                System.out.printf("Decreased to %2d by worker: %2d%n",
                        counter.number, id);
            }
        }
    }

    public static void main(String[] args) {
        Counter counter = new Counter(10);
        ExecutorService executor = Executors.newCachedThreadPool();
        for (int i = 1; i <= 20; ++i) {
            executor.execute(new Worker(counter));
        }
        executor.shutdown();

        System.out.println("Number actually is: " + counter.number);
    }
}

When we run the above code we’ll see output similar to the following, which is sorted for readability:

Starting worker: 1
Starting worker: 2
Starting worker: 3
Starting worker: 4
Starting worker: 5
Starting worker: 6
Starting worker: 7
Starting worker: 8
Starting worker: 9
Starting worker: 10
Starting worker: 11
Starting worker: 12
Starting worker: 13
Starting worker: 14
Starting worker: 15
Starting worker: 16
Starting worker: 17
Starting worker: 18
Starting worker: 19
Starting worker: 20
Decreased to  9 by worker:  1
Decreased to  8 by worker:  2
Decreased to  7 by worker:  1
Decreased to  6 by worker: 20
Decreased to  5 by worker: 19
Decreased to  4 by worker: 18
Decreased to  3 by worker: 17
Decreased to  2 by worker: 15
Decreased to  1 by worker: 14
Decreased to  0 by worker: 16
Decreased to -1 by worker: 13
Decreased to -2 by worker: 12
Number actually is: -2

As you can see from the last lines of the output, the threads have gone too far and did too much work – decreasing the number below the limit! The reason is how multi-threading environment works. When a thread is executing code of dec() method, the following may happen:

  1. Thread X takes current value of number and does comparison in step 1.
  2. When it is between steps 1 and 2 the thread scheduler may preempt (temporarily interrupt/suspend/hang) Thread X – with the state of date it sees – and allow Thread Y to execute. In such case Thread Y may do step 1 with the current value of number (which hasn’t changed yet) and even execute the rest of the code, including step 2, effectively changing the number. After that Thread Scheduler may preempt Thread Y and resume execution of Thread X from where it has been interrupted.
  3. Thread X executes step 2, but the value of number is already different than it was is step 1, because it has been changed by Thread Y. At the end it causes unexpected result as can be seen it the above program – workers 13 and 12 executed step 2 even though the limit have been already reached.

Solution:

The solution is simple: steps 1 and 2 should be executed together, without interruptions in between.

To do this in Java we can synchronize operations that should be done together, by adding the synchronized keyword to all methods that are read or write shared state. In this case there is only one method, so we’ll the multi-threaded code like so:

public synchronized boolean dec() {
   //...
}

And here’s the result of running the code with added correct synchronization (again results sorted for readability):

Starting worker: 1
Starting worker: 2
Starting worker: 3
Starting worker: 4
Starting worker: 5
Starting worker: 6
Starting worker: 7
Starting worker: 8
Starting worker: 9
Starting worker: 10
Starting worker: 11
Starting worker: 12
Starting worker: 13
Starting worker: 14
Starting worker: 15
Starting worker: 16
Starting worker: 17
Starting worker: 18
Starting worker: 19
Starting worker: 20
Decreased to  9 by worker:  1
Decreased to  8 by worker:  1
Decreased to  7 by worker:  3
Decreased to  6 by worker:  2
Decreased to  5 by worker:  3
Decreased to  4 by worker:  1
Decreased to  3 by worker:  1
Decreased to  2 by worker: 20
Decreased to  1 by worker: 19
Decreased to  0 by worker: 18
Number actually is: 0

As you can see, contrary to the previous output, now threads cannot decrease the number below limit, because step 1: comparison and step2: modification are in the same synchronized block (called critical section) preventing other threads from changing the number in between.

Every Java object have a lock, that guards access to object’s critical sections. It simply means that a Thread that has obtained the lock can do its work in isolation until it has the lock and other threads have to wait. Adding synchronized to Java methods means that when a Thread will start executing the method, it will try to obtain object’s lock and, if no other thread is executing that code, the Thread will be able to enter the method and execute it in isolation – in our code it will execute both steps in isolation.

In further posts we’ll show other examples of Java synchronization in action. :-)

Share with the World!