Java Synchronized with examples

When building java applications with sizeable functionalities, dealing with shared resources being updated by multiple threads becomes a challenge. The shared resources include commonly accessed variables like counters which need to be updated by one thread at a time e.g. in a city, multiple threads receiving real-time input from street cameras will be trying to update a common counter variable which holds the number of vehicles in the city by adding(incrementing the counter) vehicles getting into the city via inbound roads and subtracting(decrementing the counter) vehicles leaving the city via outbound roads simultaneously.

Synchronization is by use of synchronized keyword.

There are four ways of guarding shared resources in Java.

1.Synchronized instance methods.

2.Synchronized code blocks in instance methods.

3.Synchronized static methods.

4.Synchronized code blocks in static methods

1.Synchronized instance methods

This approach entails making an object’s method synchronized by applying the synchronized keyword in the method declaration.This has the effect that when multiple(two or more) threads need to access the method,they need to acquire an intrinsic lock to the object.Once one thread acquires a lock to the object,it can execute the code in the  method without having to worry about another thread executing the same code simultaneously .When it exits, all the changes made to the object’s variables by the synchronized method are visible to all other threads waiting to get the lock in a happens-before relationship.

 

Example 1

In the code below,we have a program that simulates a messaging service.Our message server symbolized by the MessageServer class receives messages from several threads and sends the messages.The message processing can take some time as destination routing parameters have to be checked,connections established and messages validated before being send out.This is simulated by a sleep of 50ms.Once a message is sent,a counter processedMessagesCount is incremented to keep track of send out messages at the end of the batch processing.

package devsought;

 

import java.time.Duration;

import java.time.Instant;

import java.util.Date;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.TimeUnit;

import java.util.logging.Level;

import java.util.logging.Logger;

 

public class JavaSynchronizationExample1 {

//thread safety of System.out.println-->https://stackoverflow.com/questions/9459657/is-multi-thread-output-from-system-out-println-interleaved

 

    public static void main(String... args) throws InterruptedException {

        int TASK_SIZE = 500;

        MessageServer sharedServer = new MessageServer();

        ExecutorService executorService = Executors.newFixedThreadPool(TASK_SIZE);

        Instant start = Instant.now();

        for (int i = 0; i < TASK_SIZE; i++) {

            executorService.submit(new MessagingTask("Message " + (i + 1), "Thread " + (i + 1), sharedServer));

        }

 

        executorService.shutdown();

        executorService.awaitTermination(1l, TimeUnit.HOURS);

        System.out.println("Processed Messages:" + sharedServer.getProcessedMessagesCount());

 

        Instant end = Instant.now();

        Duration timeElapsed = Duration.between(start, end);

        System.out.println("Time taken: " + timeElapsed.toMillis() + " milliseconds");

    }

}

 

class MessagingTask implements Callable<Boolean> {

 

    private String message;

    private String threadName;

    private MessageServer messageServer;

 

    public MessagingTask(String message, String threadName, MessageServer messageServer) {

        this.message = message;

        this.threadName = threadName;

        this.messageServer = messageServer;

    }

 

    @Override

    public Boolean call() throws Exception {

        messageServer.sendMessage(message, threadName);

        return true;

    }

}

 

class MessageServer {

 

    private int processedMessagesCount;

 

    public void sendMessage(String message, String threadName) {

 

        try {

            //simulate a long task

            Thread.sleep((long) (Math.random() * 50));

            //increment message count

            processedMessagesCount++;

 

           // System.out.println("Processing message ->" + message + ", from thread -->" + threadName + ", at number --->" + getProcessedMessagesCount());

        } catch (InterruptedException ex) {

      

            Logger.getLogger(MessageServer.class.getName()).log(Level.SEVERE, null, ex);

        }

 

    }

 

    /**

     * @return the processedMessages

     */

    public int getProcessedMessagesCount() {

        return processedMessagesCount;

    }

 

}

 

Output

Processed Messages:475

Time taken: 150 milliseconds

Explanation:

The program outputs that 475 messages were processed instead of 500 messages!

This is because as different threads are sharing one instance of our message server and after having their messages send out, there were instances where multiple threads incremented the processedMessagesCount simultaneously. This happened 25 times .Note while this seems a trivial problem, in a real messaging server application, this can have more serious implications like sending of messages to the wrong receiver or even delivering the wrong message for instance if the message server stored the destination, the outgoing message in its instance variables while processing as multiple threads will overwrite each other’s data and result into more chaos.

In another run of the program, a different result will be obtained. Below is sample output in a second run of the program.

Processed Messages:477

Time taken: 155 milliseconds

The solution to this is discussed in example 2 below.

 

Example 2

package devsought2;

 

import java.time.Duration;

import java.time.Instant;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.TimeUnit;

import java.util.logging.Level;

import java.util.logging.Logger;

 

public class JavaSynchronizationExample2 {

//thread safety of System.out.println-->https://stackoverflow.com/questions/9459657/is-multi-thread-output-from-system-out-println-interleaved

 

    public static void main(String... args) throws InterruptedException {

        int TASK_SIZE = 500;

        MessageServer sharedServer = new MessageServer();

        ExecutorService executorService = Executors.newFixedThreadPool(TASK_SIZE);

        Instant start = Instant.now();

        for (int i = 0; i < TASK_SIZE; i++) {

            executorService.submit(new MessagingTask("Message " + (i + 1), "Thread " + (i + 1), sharedServer));

        }

 

        executorService.shutdown();

        executorService.awaitTermination(1l, TimeUnit.HOURS);

        System.out.println("Processed Messages:" + sharedServer.getProcessedMessagesCount());

        Instant end = Instant.now();

        Duration timeElapsed = Duration.between(start, end);

        System.out.println("Time taken: " + timeElapsed.toMillis() + " milliseconds");

    }

}

 

class MessagingTask implements Callable<Boolean> {

 

    private String message;

    private String threadName;

    private MessageServer messageServer;

 

    public MessagingTask(String message, String threadName, MessageServer messageServer) {

        this.message = message;

        this.threadName = threadName;

        this.messageServer = messageServer;

    }

 

    @Override

    public Boolean call() throws Exception {

        messageServer.sendMessage(message, threadName);

        return true;

    }

}

 

class MessageServer {

 

    private int processedMessagesCount;

 

    public synchronized void sendMessage(String message, String threadName) {

 

        try {

            //simulate a long task

            Thread.sleep((long) (Math.random() * 50));

            //increment message count

            processedMessagesCount++;

 

           // System.out.println("Processing message ->" + message + ", from thread -->" + threadName + ", at number --->" + getProcessedMessagesCount());

        } catch (InterruptedException ex) {

      

            Logger.getLogger(MessageServer.class.getName()).log(Level.SEVERE, null, ex);

        }

 

    }

 

    /**

     * @return the processedMessages

     */

    public int getProcessedMessagesCount() {

        return processedMessagesCount;

    }

 

}

 

Output

Processed Messages:500

Time taken: 13473 milliseconds

Explanation:

Access to the sendMessage method of MessageServer is synchronized as below

 

public synchronized void sendMessage(String message, String threadName) {

        try {

            //simulate a long task

            Thread.sleep((long) (Math.random() * 50));

            //increment message count

            processedMessagesCount++;

 

           // System.out.println("Processing message ->" + message + ", from thread -->" + threadName + ", at number --->" + getProcessedMessagesCount());

        } catch (InterruptedException ex) {

      

            Logger.getLogger(MessageServer.class.getName()).log(Level.SEVERE, null, ex);

        }

 

    }

 

With this implementation, only one thread at a time will access the sendMessage method. This results in consistent updates to the processedMessagesCount variable and we end up with 500 which is the expected number of processed messages. However , this implementation is not optimal as each thread has to wait until all the code in the synchronized method is fully executed by the thread which has the lock, one after the other. This evident by the long execution time of 13473 milliseconds compared to outputs of the unsynchronized version in example which took 150 milliseconds  and 155 milliseconds respectively. The solution is demonstrated in Example 3 under synchronized code blocks.

2.Synchnronized code blocks in instance methods

Instead of synchronizing an entire method, we only synchronize the section of code that needs thread safety. In our example , this is the section where we update the processedMessagesCount counter variable. This means multiple threads accessing the same object can execute other parts of the unsynchronized method code e.g. the section where we simulate a long task and only wait on the section which is synchronized.

 

This approach speeds up program execution as unnecessary locking is avoided. However, use it carefully by making sure that code in the unsynchronized section is thread safe .

When synchronizing an instance method,the lock used is of the object and this implies use of the this (the object reference implying this instance which owns the method)keyword as  a parameter to the synchronized section. Another approach would be to pass a declared object reference variable to the same section .

Example 3

package devsought3;

 

import java.time.Duration;

import java.time.Instant;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.TimeUnit;

import java.util.logging.Level;

import java.util.logging.Logger;

 

public class JavaSynchronizationExample3 {

//thread safety of System.out.println-->https://stackoverflow.com/questions/9459657/is-multi-thread-output-from-system-out-println-interleaved

 

    public static void main(String... args) throws InterruptedException {

        int TASK_SIZE = 500;

        MessageServer sharedServer = new MessageServer();

        ExecutorService executorService = Executors.newFixedThreadPool(TASK_SIZE);

        Instant start = Instant.now();

        for (int i = 0; i < TASK_SIZE; i++) {

            executorService.submit(new MessagingTask("Message " + (i + 1), "Thread " + (i + 1), sharedServer));

        }

 

        executorService.shutdown();

        executorService.awaitTermination(1l, TimeUnit.HOURS);

        System.out.println("Processed Messages:" + sharedServer.getProcessedMessagesCount());

        Instant end = Instant.now();

        Duration timeElapsed = Duration.between(start, end);

        System.out.println("Time taken: " + timeElapsed.toMillis() + " milliseconds");

    }

}

 

class MessagingTask implements Callable<Boolean> {

 

    private String message;

    private String threadName;

    private MessageServer messageServer;

 

    public MessagingTask(String message, String threadName, MessageServer messageServer) {

        this.message = message;

        this.threadName = threadName;

        this.messageServer = messageServer;

    }

 

    @Override

    public Boolean call() throws Exception {

        messageServer.sendMessage(message, threadName);

        return true;

    }

}

 

class MessageServer {

 

    private int processedMessagesCount;

 

    public void sendMessage(String message, String threadName) {

 

        try {

            //simulate a long task

            Thread.sleep((long) (Math.random() * 50));

            //increment message count

            synchronized (this) {

                processedMessagesCount++;

            }

            //System.out.println("Processing message ->" + message + ", from thread -->" + threadName + ", at number --->" + getProcessedMessagesCount());

        } catch (InterruptedException ex) {

         

 

            Logger.getLogger(MessageServer.class.getName()).log(Level.SEVERE, null, ex);

        }

 

    }

 

    /**

     * @return the processedMessages

     */

    public int getProcessedMessagesCount() {

        return processedMessagesCount;

    }

 

}

 

Output

Processed Messages:500

Time taken: 170 milliseconds

 

Explanation:

In this run of the program, we can see that 500 messages were processed which is correct. The execution took 170 milliseconds which is incredibly fast compared to approach 2 of using synchronized methods.

 

3 .Synchronized static methods

Synchronization in static methods is same as synchronization in instance methods.

Sample implementation:

public static  synchronized void sendMessage(String message, String threadName) {

 

        try {

            //simulate a long task

            Thread.sleep((long) (Math.random() * 50));

            //increment message count

            processedMessagesCount++;

 

           // System.out.println("Processing message ->" + message + ", from thread -->" + threadName + ", at number --->" + getProcessedMessagesCount());

        } catch (InterruptedException ex) {

      

            Logger.getLogger(MessageServer.class.getName()).log(Level.SEVERE, null, ex);

        }

 

    }

 

4. Synchronized code blocks in static methods

Synchronization is on the Class object associated with the class because a static method belongs to a class not an object.

The implementation is as below:

public static void sendMessage(String message, String threadName) {

 

        try {

            //simulate a long task

            Thread.sleep((long) (Math.random() * 50));

            //increment message count

            synchronized (MessageServer.class) {

                processedMessagesCount++;

            }

            System.out.println("Processing message ->" + message + ", from thread -->" + threadName + ", at number --->" + getProcessedMessagesCount());

        } catch (InterruptedException ex) {

           

 

            Logger.getLogger(MessageServer.class.getName()).log(Level.SEVERE, null, ex);

        }

 

    }

 

Full program code:

package devsought4;

 

import java.time.Duration;

import java.time.Instant;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.TimeUnit;

import java.util.logging.Level;

import java.util.logging.Logger;

 

public class JavaSynchronizationExample4 {

//thread safety of System.out.println-->https://stackoverflow.com/questions/9459657/is-multi-thread-output-from-system-out-println-interleaved

 

    public static void main(String... args) throws InterruptedException {

        int TASK_SIZE = 500;

 

        ExecutorService executorService = Executors.newFixedThreadPool(TASK_SIZE);

        Instant start = Instant.now();

        for (int i = 0; i < TASK_SIZE; i++) {

            executorService.submit(new MessagingTask("Message " + (i + 1), "Thread " + (i + 1)));

        }

 

        executorService.shutdown();

        executorService.awaitTermination(1l, TimeUnit.HOURS);

        System.out.println("Processed Messages:" + MessageServer.getProcessedMessagesCount());

        Instant end = Instant.now();

        Duration timeElapsed = Duration.between(start, end);

        System.out.println("Time taken: " + timeElapsed.toMillis() + " milliseconds");

    }

}

 

class MessagingTask implements Callable<Boolean> {

 

    private String message;

    private String threadName;

 

    public MessagingTask(String message, String threadName) {

        this.message = message;

        this.threadName = threadName;

    }

 

    @Override

    public Boolean call() throws Exception {

        MessageServer.sendMessage(message, threadName);

        return true;

    }

}

 

class MessageServer {

 

    private static int processedMessagesCount;

 

    public static void sendMessage(String message, String threadName) {

 

        try {

            //simulate a long task

            Thread.sleep((long) (Math.random() * 50));

            //increment message count

            synchronized (MessageServer.class) {

                processedMessagesCount++;

            }

          //  System.out.println("Processing message ->" + message + ", from thread -->" + threadName + ", at number --->" + getProcessedMessagesCount());

        } catch (InterruptedException ex) {

           

 

            Logger.getLogger(MessageServer.class.getName()).log(Level.SEVERE, null, ex);

        }

 

    }

 

    /**

     * @return the processedMessages

     */

    public static int getProcessedMessagesCount() {

        return processedMessagesCount;

    }

 

}

Output

Processed Messages:500

Time taken: 156 milliseconds.

The output shows 500 messages which is the correct output, processed and 156 ms which is faster than if a fully synchronized static method implementation was used.

 

Tips:

1.Use synchronized keyword to guard variables that need consistency across different/multiple thread updates

2.Use synchronized code blocks to improve performance to avoid creating un necessary locking scenarios by blocking whole methods.

The source codes used in the above examples are available on Github.

About the Author - John Kyalo Mbindyo(Bsc Computer Science) is a Senior Application Developer currently working at NCBA Bank Group,Nairobi- Kenya.He is passionate about making programming tutorials and sharing his knowledge with other software engineers across the globe. You can learn more about him and follow him on  Github.